# How to install Matomo on Ubuntu or Debian with Nginx and MariaDB

> Install Matomo web analytics with Nginx, PHP-FPM and MariaDB on Ubuntu or Debian, add HTTPS, cron archiving, GeoIP and privacy settings, then back it up.

Difficulty: Intermediate\
Tested on: Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 12, Debian 13

Matomo is an open-source web analytics platform that you run on your own server, so visitor data stays in your own database instead of a third-party service. It offers reports similar to commercial analytics tools, plus privacy controls such as IP anonymisation, opt-out and consent tracking.

This guide installs **Matomo On-Premise the classic way that Matomo's installation guide describes**: Nginx with the configuration that Matomo publishes, PHP-FPM and MariaDB from your distribution, the official `matomo.zip` release and a Let's Encrypt certificate from Certbot. You then switch report processing to a cron job, set up geolocation and privacy options, and learn how to back up, restore and update Matomo. At the time of writing the current release is Matomo 5.14.1; Matomo 6.0.0 is in beta.

> **Note**
>
> Matomo also maintains an official Docker project, [matomo-org/docker](https://github.com/matomo-org/docker), published as the `matomo` image on Docker Hub. This guide uses the classic installation because it is the method Matomo's own installation and update guides document step by step.

## Prerequisites

- A server running **Ubuntu 24.04 LTS**, **Ubuntu 26.04 LTS**, **Debian 12** or **Debian 13**.
- A non-root user with `sudo` rights. If you have not set one up yet, follow [Secure a new Linux server](/guides/secure-a-new-linux-server) and [Set up SSH keys](/guides/ssh-keys).
- A domain name such as `matomo.example.com` with an A record (and AAAA record if you use IPv6) pointing at your server.
- Ports 80 and 443 free on the server. This guide runs Nginx as the web server; if the server already uses Caddy or another web server on these ports, install Matomo on a separate server or adapt the configuration yourself.

Matomo publishes recommended server sizes by traffic rather than a strict minimum. The first column shows its recommendation for up to 100,000 page views a month on a single server; the second shows its figure for up to 1 million page views a month:

| Resource | Minimum (official) | Suggested starting point |
|---|---|---|
| CPU | 2 CPU (up to 100,000 page views a month) | 4 CPU for up to 1 million page views a month |
| Memory | 2 GB RAM | 8 GB RAM for up to 1 million page views a month |
| Disk | 50 GB SSD | 250 GB SSD for up to 1 million page views a month |
| PHP | PHP 8 for Matomo 5; 8.1 or newer for Matomo 6 | The PHP 8 release from your distribution |
| Database | MySQL 5.5+ or MariaDB for Matomo 5; MySQL 8.0+ or MariaDB 10.6+ for Matomo 6 | MariaDB from your distribution |

Above about 1 million tracked actions a month, Matomo advises splitting the database and the application onto two servers.

## Step 1 — Install Nginx, PHP-FPM and MariaDB

Install the web server, the database, PHP-FPM and the PHP extensions that Matomo requires or recommends (`pdo_mysql` or `mysqli`, plus `curl`, `gd`, `xml`, `mbstring`, `intl` and `zip`):

```bash
sudo apt update
sudo apt install nginx mariadb-server php-fpm php-cli php-mysql php-curl php-gd php-xml php-mbstring php-intl php-zip unzip curl
```

Installing `php-fpm` instead of the `php` metapackage keeps Apache off the server. Check the versions and find the PHP-FPM socket name, which contains the PHP version:

```bash
php -v
systemctl status nginx mariadb --no-pager
ls /run/php/
```

`php -v` should report PHP 8, and `/run/php/` should contain a socket such as `php8.3-fpm.sock`. The version number differs between Ubuntu and Debian releases; you will use it in Step 5.

## Step 2 — Create the database and user

Matomo recommends a dedicated database with a user that can access only that database. Generate a strong password first and keep it for the web installer:

```bash
openssl rand -hex 24
sudo mariadb
```

On Ubuntu and Debian, `sudo mariadb` logs you in as the database root user through the local socket. Run these statements, replacing `change-me` with the generated password:

```sql
CREATE DATABASE matomo;
CREATE USER 'matomo'@'localhost' IDENTIFIED BY 'change-me';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, DROP, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON matomo.* TO 'matomo'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```

These are the privileges listed in Matomo's database FAQ. Matomo also mentions an optional global `FILE` privilege that speeds up archiving with `LOAD DATA INFILE`; this guide leaves it out to keep the database user limited to its own database.

## Step 3 — Download Matomo

Download the official release from `builds.matomo.org`, unpack it and move it to `/var/www/matomo`. Matomo asks you not to use third-party download sources, and it publishes GPG signatures if you want to verify the archive:

```bash
cd /tmp
curl -fLO https://builds.matomo.org/matomo.zip
unzip -q matomo.zip
sudo mv /tmp/matomo /var/www/matomo
sudo chown -R www-data:www-data /var/www/matomo
ls /var/www/matomo
```

You should see folders such as `config`, `core`, `plugins` and `tmp`, and the `console` script. Making `www-data` the owner of the whole folder lets Matomo write to `tmp/` and `config/`, download geolocation databases to `misc/`, and use its one-click updater.

> **Tip**
>
> If you prefer stricter permissions, Matomo only needs write access to `tmp/`, `config/config.ini.php`, `misc/user` and the tracker files `matomo.js` and `piwik.js`. In that case set `enable_auto_update = 0` in the `[General]` section of `config/config.ini.php` and update manually as shown in the update section.

## Step 4 — Open the firewall and get a certificate

Allow SSH and web traffic, then install Certbot with its Nginx plugin:

```bash
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo apt install certbot python3-certbot-nginx
```

This uses the Certbot packages from your distribution, which include a renewal timer. The Certbot team recommends its snap instead, as shown in [Nginx reverse proxy with Certbot](/guides/nginx-reverse-proxy-certbot); either works, but install only one of them.

Request a certificate for your Matomo domain. `certonly` obtains the certificate without changing your Nginx files, and the deploy hook reloads Nginx after every renewal so the new certificate is used:

```bash
sudo certbot certonly --nginx -d matomo.example.com --deploy-hook "systemctl reload nginx"
sudo ls /etc/letsencrypt/live/matomo.example.com/
sudo certbot renew --dry-run
```

You should see `fullchain.pem` and `privkey.pem`, and the dry run should report that renewal would succeed. For more background on Certbot, see [Nginx with Certbot](/guides/nginx-reverse-proxy-certbot).

## Step 5 — Configure Nginx with Matomo's configuration

Matomo publishes an Nginx configuration in the [matomo-nginx](https://github.com/matomo-org/matomo-nginx) repository. It only passes Matomo's entry scripts to PHP, returns 403 for every other PHP file and blocks the `config`, `tmp`, `core`, `lang`, `libs`, `vendor`, `plugins` and `misc` folders.

First create the TLS settings snippet. It uses the session and cipher values from Matomo's `ssl.conf`, adds TLS 1.3 and leaves out OCSP stapling, because Let's Encrypt stopped running OCSP responders in August 2025:

```bash
sudo nano /etc/nginx/snippets/matomo-ssl.conf
```

```nginx
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
```

Then create the site file:

```bash
sudo nano /etc/nginx/sites-available/matomo.conf
```

Paste Matomo's configuration with your domain, certificate paths, the snippet and the install path filled in:

```nginx
server {
    listen [::]:80;
    listen 80;
    server_name matomo.example.com;
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen [::]:443 ssl http2;
    listen 443 ssl http2;
    server_name matomo.example.com;
    access_log /var/log/nginx/matomo.access.log;
    error_log /var/log/nginx/matomo.error.log;

    ssl_certificate /etc/letsencrypt/live/matomo.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matomo.example.com/privkey.pem;
    include snippets/matomo-ssl.conf;

    add_header Referrer-Policy origin always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    root /var/www/matomo/;
    index index.php;

    location ~ ^/(index|matomo|piwik|js/index|plugins/HeatmapSessionRecording/configs)\.php$ {
        include snippets/fastcgi-php.conf;
        try_files $fastcgi_script_name =404;
        fastcgi_param HTTP_PROXY "";
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    location ~* ^.+\.php$ {
        deny all;
        return 403;
    }

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ ^/(config|tmp|core|lang) {
        deny all;
        return 403;
    }

    location ~ /\.ht {
        deny all;
        return 403;
    }

    location ~ js/container_.*_preview\.js$ {
        expires off;
        add_header Cache-Control 'private, no-cache, no-store';
    }

    location ~ \.(gif|ico|jpg|png|svg|js|css|htm|html|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2)$ {
        allow all;
        expires 1h;
        add_header Pragma public;
        add_header Cache-Control "public";
    }

    location ~ ^/(libs|vendor|plugins|misc|node_modules) {
        deny all;
        return 403;
    }

    location ~/(.*\.md|LEGALNOTICE|LICENSE) {
        default_type text/plain;
    }
}
```

Point `fastcgi_pass` at the versioned PHP-FPM socket you saw in Step 1, enable the site, test the configuration and reload Nginx:

```bash
PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
sudo sed -i "s#/run/php/php-fpm.sock#/run/php/php${PHPV}-fpm.sock#" /etc/nginx/sites-available/matomo.conf
grep fastcgi_pass /etc/nginx/sites-available/matomo.conf
sudo ln -s /etc/nginx/sites-available/matomo.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
curl -I https://matomo.example.com/
```

`nginx -t` should end with "test is successful", and `curl` should return `HTTP/2 200` for the installer page.

> **Note**
>
> Matomo's file uses `listen 443 ssl http2;`. Nginx 1.25.1 and newer, shipped by newer distribution releases, print a warning that this form of the `http2` parameter is deprecated. The warning is harmless; on those versions you can instead write `listen 443 ssl;` and add `http2 on;` inside the server block.

## Step 6 — Run the web installer

Open `https://matomo.example.com/` in your browser right away: until the installer is finished, anyone who reaches the address could complete it. Follow the screens:

1. **Welcome** and **System check**: fix anything marked as an error, then continue.
2. **Database setup**: database server `localhost` (so PHP connects through the local socket, matching the `'matomo'@'localhost'` account), login `matomo`, the password from Step 2, database name `matomo`. Keep the default table prefix and adapter.
3. **Superuser**: create the single Superuser with a unique username and a long password.
4. **First website**: enter the name and URL of the site you want to track.
5. **Tracking code**: copy the JavaScript snippet and add it to every page of your site, ideally just before the closing `head` tag or in a shared header template.

Click **Continue to Matomo** and sign in. Now force HTTPS in Matomo itself. Open the configuration file that the installer wrote:

```bash
sudo nano /var/www/matomo/config/config.ini.php
```

Find the existing `[General]` section and add these two lines inside it, below the lines that are already there:

```ini
force_ssl = 1
browser_archiving_disabled_enforce = 1
```

`force_ssl` redirects every Matomo request to HTTPS. `browser_archiving_disabled_enforce` stops reports from being processed when someone views them, which the cron job in the next step takes over. Finally, turn on two-factor authentication for your account under **Administration → Personal → Security** and store the recovery codes safely.

Matomo sends email reports, user invitations and password reset links through the SMTP server you set under **Administration → System → General settings** in the **Email server settings** section, for example host `smtp.example.com` on port `587` with STARTTLS; test it with `sudo -u www-data php /var/www/matomo/console core:test-email admin@example.com`.

> **Note**
>
> Outbound port 25 is closed by default on HyperDC VPS. For services bought for a term of 3 months or longer, it is opened on request: [open a support ticket](/guides/support-tickets). Until then, send mail through an SMTP relay on port 587.

## Step 7 — Process reports with cron

In **Administration → System → General settings**, set **Archive reports when viewed from the browser** to **No** and **Archive reports at most every X seconds** to `3600`, then save. Create a log folder outside the web root and test the archiver as the web server user:

```bash
sudo install -d -o www-data -g www-data -m 750 /var/log/matomo
sudo -u www-data /usr/bin/php /var/www/matomo/console core:archive --matomo-domain=https://matomo.example.com
```

A successful run ends with a summary that reports no errors. `--matomo-domain` replaces the older `--url` option, which Matomo deprecated in version 5. Now schedule the archiver to run at five minutes past every hour, as Matomo recommends:

```bash
sudo nano /etc/cron.d/matomo-archive
```

```text
5 * * * * www-data /usr/bin/php /var/www/matomo/console core:archive --matomo-domain=https://matomo.example.com > /var/log/matomo/archive.log 2>&1
```

The sixth field runs the job as `www-data`, so the files it creates have the same owner as the rest of Matomo. Check `/var/log/matomo/archive.log` after the next full hour. If a run ever takes longer than an hour, Matomo suggests running it less often, for example every 2, 6 or 12 hours.

## Step 8 — Set up geolocation

Matomo looks up each visitor's country, region and city in a geolocation database stored on your own server, so IP addresses are not sent to an outside service for this. Go to **Administration → System → Geolocation**:

1. In the section **Setup automatic updates of GeoIP databases** at the bottom of the page, let Matomo download the free DB-IP city database, or enter the download URL of a MaxMind GeoIP2 database if you have a MaxMind account.
2. Choose an update period, weekly or monthly. The updates run as part of the archiving cron job from Step 7.
3. Select the provider **DBIP / GeoIP 2 (Php)** and save. It should show as **Installed**.

The database is stored in `/var/www/matomo/misc/`, which is why that folder must be writable by `www-data`. New visits are located from now on; earlier visits keep the location they were given when they were tracked.

## Step 9 — Review the privacy settings

Matomo offers several privacy controls under **Administration → Privacy**. Which ones you need depends on the rules that apply to your websites; this guide only describes what each one does.

- **Anonymize data**: select **Anonymize Visitors' IP addresses** and choose how many bytes to mask (1, 2 or 3, or full masking). You also decide whether geolocation uses the masked address (higher privacy, lower accuracy) or the full one. The same page can replace User IDs with a pseudonym and **Regularly delete old raw data** while keeping aggregated reports.
- **Users opt-out**: copy the opt-out form code into your privacy page. Visitors who opt out get the `mtm_consent_removed` cookie and are no longer tracked. Matomo marks the **Support Do Not Track preference** option as deprecated.
- **Consent**: if your site asks for consent, add `_paq.push(['requireConsent']);` (no tracking until consent) or `_paq.push(['requireCookieConsent']);` (tracking without cookies until consent) at the top of the tracking code, before `trackPageView`, and call the matching `setConsentGiven` or `rememberConsentGiven` functions from your consent banner. To track without any cookies, add `_paq.push(['disableCookies']);` before `trackPageView`.
- **GDPR Overview** and **GDPR Tools**: find, export or delete the data of an individual visitor.

## Back up and restore

A complete Matomo backup has two parts: the MariaDB database, which holds all tracking data and reports, and the Matomo folder, especially `config/config.ini.php`, which contains the database login, the salt and the list of enabled plugins. Matomo's backup FAQ uses `mysqldump` with these options; on MariaDB the same tool is called `mariadb-dump`:

```bash
sudo mkdir -p /opt/backups && sudo chown $USER:$USER /opt/backups && chmod 700 /opt/backups
sudo mariadb-dump --extended-insert --no-autocommit --quick --single-transaction matomo | gzip > /opt/backups/matomo-db-$(date +%F).sql.gz
sudo tar czf /opt/backups/matomo-files-$(date +%F).tar.gz --exclude=matomo/tmp -C /var/www matomo
ls -lh /opt/backups
```

The dump contains visitor data, so keep it private and copy it off the server. For databases larger than about 10 GB or more than a million actions a month, Matomo suggests Percona XtraBackup instead of a dump.

To restore on a new server, repeat Steps 1 to 5 (packages, database and user, certificate and Nginx), but unpack your file backup instead of downloading a fresh `matomo.zip`. Then import the database dump:

> **Warning**
>
> Importing a dump into an existing Matomo database overwrites its tables. Restore into an empty database, or back up the current one first.

```bash
sudo tar xzf /opt/backups/matomo-files-2026-10-09.tar.gz -C /var/www
sudo install -d -o www-data -g www-data /var/www/matomo/tmp
sudo chown -R www-data:www-data /var/www/matomo
gunzip -c /opt/backups/matomo-db-2026-10-09.sql.gz | sudo mariadb matomo
```

Use the same database name, user and password as in the restored `config.ini.php`, re-create `/etc/cron.d/matomo-archive`, and open Matomo to check that your reports are back.

## Update Matomo

Read the [changelog](https://matomo.org/changelog/) and back up the database and `config/config.ini.php` before every update. When a new version is available, Matomo shows a notice in **Administration** to Superusers; with the ownership from Step 3, you can apply it with the one-click update button.

To update from the command line instead, which Matomo recommends for busier servers, download the new release, copy it over the existing files and run the database upgrade. The archive does not contain `config/config.ini.php`, so your configuration is kept:

```bash
cd /tmp
rm -rf /tmp/matomo /tmp/matomo.zip
curl -fLO https://builds.matomo.org/matomo.zip
unzip -q matomo.zip
sudo cp -a /tmp/matomo/. /var/www/matomo/
sudo chown -R www-data:www-data /var/www/matomo
sudo -u www-data php /var/www/matomo/console core:update
sudo -u www-data php /var/www/matomo/console diagnostics:unexpected-files
```

The last command lists files left over from older versions; after reviewing the list, remove them with `diagnostics:unexpected-files --delete`. Afterwards open **Administration → Diagnostics → System check** and follow its recommendations. Keep the OS packages current with `sudo apt update && sudo apt upgrade` as well.

Matomo 6 requires PHP 8.1 or newer and MySQL 8.0 or MariaDB 10.6 or newer. Check `php -v` and `mariadb --version` before you move to it, and read its release notes.

## Troubleshooting

### 502 Bad Gateway

Nginx cannot reach PHP-FPM. Compare the socket in `grep fastcgi_pass /etc/nginx/sites-available/matomo.conf` with the file in `ls /run/php/`, fix the path, then run `sudo nginx -t && sudo systemctl reload nginx`. Check that PHP-FPM runs with `systemctl status 'php*-fpm' --no-pager`.

### nginx -t reports that it cannot load the certificate

The certificate for the domain was not issued yet, or the domain in the file does not match it. Run `sudo certbot certificates` to list existing certificates, fix the DNS record if the challenge failed, and repeat Step 4 before reloading Nginx.

### The system check says tmp/ or config/ is not writable

The files are not owned by the PHP-FPM user. Run `sudo chown -R www-data:www-data /var/www/matomo` and reload the page. If you chose the stricter permissions from the tip in Step 3, make sure `tmp/` and `config/config.ini.php` are still writable.

### Reports stay empty or are only updated once a day

Browser archiving is off but the cron job is not running. Check `/var/log/matomo/archive.log` and `grep CRON /var/log/syslog` (or `journalctl -u cron` on Debian), and run the command from Step 7 by hand as `www-data` to see the error. Make sure `/etc/cron.d/matomo-archive` ends with a newline.

### Matomo warns that it is accessed from an untrusted host

Matomo only accepts the hostnames it knows. If you reach it under a new name, add a line such as `trusted_hosts[] = "matomo.example.com"` to the `[General]` section of `config/config.ini.php`.

## Next steps

- Compare a lighter, cookie-free alternative: [How to install Umami](/guides/install-umami) or [Plausible Community Edition](/guides/install-plausible-ce).
- Learn more about Certbot and Nginx in [Nginx with Certbot](/guides/nginx-reverse-proxy-certbot).
- Track a WordPress site you host yourself: [WordPress on a LEMP stack](/guides/install-wordpress-lemp).
- See servers for self-hosted analytics on the [web analytics hosting](/web-analytics-hosting) page.
- Harden the installation further with Matomo's [security recommendations](https://matomo.org/faq/on-premise/how-to-configure-matomo-for-security/).

## Frequently asked questions

### Should I install Matomo with Docker or the classic way?

Both are official. Matomo's installation guide describes the classic setup with a web server, PHP and MySQL or MariaDB, which this guide follows. The matomo-org/docker project publishes the matomo image on Docker Hub if you prefer containers.

### Why do I need a cron job for Matomo?

Matomo turns raw visits into reports in a step called archiving. Without cron, reports are processed when someone opens them, which is slow and loads the database. Matomo recommends cron archiving once a site gets more than a few hundred visits a day.

### Does Matomo anonymise IP addresses?

Matomo can mask one, two or three bytes of each IP address, or all of it, under Administration, Privacy, Anonymize data. It can still use the full address for the geolocation lookup on your own server unless you choose to use the masked one.

### Which PHP and database versions does Matomo need?

Matomo's requirements page lists PHP 8 and MySQL 5.5 or newer or MariaDB for Matomo 5. Matomo 6.0.0 requires PHP 8.1 or newer and MySQL 8.0 or MariaDB 10.6 or newer, so plan for those versions now.

### Which HyperDC servers can run Matomo?

Matomo runs on any HyperDC Linux VPS, VDS or dedicated server with root access and a supported Ubuntu or Debian release. Size it with Matomo's published recommendations for your monthly page views.

---

Source: <https://hyperdc.com/guides/tutorials/install-matomo>\
Updated: 2026-10-09
