Self-Hosting Matomo on a Small VPS, Alongside Other Services
Update: I’ve since stopped using Matomo, not because it didn’t work, but because I decided I don’t need any site analytics. The remarks below are still valid, though.
I’ve recently taken up experimenting with a small Ubuntu VPS, and while still directly hosting a few Laravel-based applications, I’ve also started fiddling with Docker (Compose) as a means to keep the host OS somewhat lean.
Two apps I’m running alongside these rather plain PHP sites, are Mastodon—itself written in Ruby and requiring Redis and PostgreSQL—and Matomo—PHP again. And thus, even though I could containerize everything, my current setup looks like this:
- NGINX, PHP, MySQL and Let’s Encrypt installed directly on the host OS
- Mastodon, and with it Redis and PostgreSQL, inside (networked) Docker containers
- Matomo—another PHP application—and a corresponding MySQL server, inside another set of Docker containers
That said, let’s have a look at some of the work that went into installing Matomo—a self-hosted alternative to Google Analytics.
Starting point was the NGINX example in the official GitHub repo. Since I didn’t want to run yet another NGINX instance, I commented out the corresponding lines in docker-compose.yml
, made sure port 9000 would be opened up to the host OS, and used the supplied matomo.conf
to add another virtual host to my existing /etc/nginx/sites-available
directory instead.
...
app:
image: matomo:fpm-alpine
restart: always
links:
- db
volumes:
- matomo:/var/www/html
environment:
- MATOMO_DATABASE_HOST=db
env_file:
- ./db.env
ports:
# Allow NGINX to easily pass PHP requests
- 9000:9000
# web:
# image: nginx:alpine
# restart: always
# volumes:
# - matomo:/var/www/html:ro
# - ./matomo.conf:/etc/nginx/conf.d/default.conf:ro
# ports:
# - 8080:80
...
The example.org virtual host server entries—I put these in a file named /etc/nginx/sites-available/matomo
and then symlink to that from /etc/nginx/sites-enabled/matomo
—look a little something like this. Note: I’m merely highlighting the rather important changes!
server {
listen 80;
listen [::]:80;
server_name example.org;
# Redirect non-https requests to https
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.org;
...
# Let's Encrypt SSL certificates
ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
# Document root, on the host OS
root /home/matomo/matomo;
...
location ~ ^/(index|matomo|piwik|js/index|plugins/HeatmapSessionRecording/configs).php {
...
# Hard-coded document root, as inside the app container it is different from the one above
fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
...
# Matomo is reachable at port 9000
fastcgi_pass 127.0.0.1:9000;
}
}
Next up was making sure the different directory owners were set correctly.
As should be clear from above server
directive, Matomo’s docker-compose.yml
is living in /home/matomo
—owned by the user of the same name—and the files actually making up the web app in /home/matomo/matomo
. For NGINX to be able to serve any (static) files, it needs read access to the latter, which it normally has.
For the container’s PHP process—which runs as the container’s “www-data” user, with UID 82—to be able to write to this directory, though, it has to be its owner. Similarly, /home/matomo/db
needs owned by the database container’s “mysql” user, UID 999.