VPS Snaps

How to back up and restore Matomo

A Matomo backup is the MySQL or MariaDB database plus the Matomo directory, above all config/config.ini.php, which holds the database password and the salt that API tokens are hashed with, and the Marketplace plugins in plugins/. Dump the database with mysqldump --single-transaction. The raw log tables cannot be rebuilt; archived reports can be re-processed only for periods whose raw logs still exist.

8 min readUpdated Checked against official documentation

What Matomo stores and where

Matomo, formerly Piwik, is open-source web analytics under the GPL v3 or later. This guide covers Matomo 5 (5.14.1 was released on 4 October 2026; Matomo 6 is in beta). Matomo lives in /var/www/matomo, PHP runs as www-data, and the database is matomo with the default table prefix matomo_.

PartWhereBack it up?
Raw log datamatomo_log_visit, matomo_log_link_visit_action, matomo_log_action, matomo_log_conversion, matomo_log_conversion_itemYes. Every visit and action. Cannot be rebuilt.
Archived reportsmatomo_archive_numeric_YYYY_MM, matomo_archive_blob_YYYY_MMYes. Rebuildable only from raw logs that still exist.
Sites, users, goals, settingsmatomo_site, matomo_user, matomo_goal, matomo_option and othersYes.
config.ini.phpconfig/Yes, on its own and encrypted.
Marketplace pluginsplugins/, beside the bundled onesYes.
Custom logo and faviconmisc/user/Yes.
GeoIP databasesmisc/*.mmdbOptional. Can be downloaded again.
Tag Manager containersjs/container_*.jsOptional. Generated from the database.
Cachestmp/No. Keep the folder.

Matomo's best-practice page asks for a full database backup every day, kept 30 days, the Matomo directory daily, and backup storage of 10 times the uncompressed database size.

Raw logs, archives, and what re-archiving can rebuild

The tracker writes raw log rows. core:archive turns them into reports, stored in two tables per month, archive_numeric for metrics and archive_blob for reports. Archives can be re-processed for any period whose raw logs remain, with core:invalidate-report-data and core:archive.

On a self-hosted install Matomo deletes nothing by default, but "Regularly delete old raw data from the database" under Administration > Privacy > Anonymize data removes logs past a cutoff. From then on, the archives are the only copy of those reports, and Matomo's FAQ warns that reprocessing them loses data. Back up the archive tables. Check what takes the space:

Terminal
mysql --defaults-extra-file=/etc/mysql/matomo-backup.cnf -e "SELECT table_name, ROUND((data_length + index_length) / 1048576) AS mb FROM information_schema.tables WHERE table_schema = 'matomo' ORDER BY data_length + index_length DESC LIMIT 10"

On a single database server above 10 GB or 1 million actions a month, Matomo's FAQ recommends Percona XtraBackup over mysqldump; with replication, it recommends dumping a replica. See XtraBackup and backing up from a replica.

Dump the database with mysqldump

Terminal
install -d -m 700 /var/backups/matomo

Copy the user and password from the [database] section of config.ini.php into an option file, so the password stays out of ps:

/etc/mysql/matomo-backup.cnf
[client]
user=matomo
password="your-password-here"
host=localhost
Terminal
chmod 600 /etc/mysql/matomo-backup.cnf
Terminal
mysqldump --defaults-extra-file=/etc/mysql/matomo-backup.cnf --single-transaction --quick --no-autocommit --no-tablespaces matomo | gzip > /var/backups/matomo/matomo-db-$(date +%F).sql.gz
  • --single-transaction reads every table from one consistent snapshot without locking, so tracking continues. Matomo creates InnoDB tables by default.
  • --quick streams rows instead of buffering whole tables, which matters for the log tables. --no-autocommit wraps each table's inserts in one transaction, which speeds up the import; Matomo's FAQ uses both.
  • --no-tablespaces avoids the global PROCESS privilege. On MariaDB 11 the program is called mariadb-dump.

A complete dump ends with -- Dump completed on. Schedule it away from core:archive: a table created or optimized during the dump breaks it (error 1412, below). The mysqldump guide covers the rest.

Keep config.ini.php and the salt

config.ini.php holds the [database] settings, trusted_hosts[], any SMTP password, and the salt the installer generated. Matomo hashes every stored token_auth with the salt and signs its cookies with it. Under a new salt, every API token stops working: reports pulled by scripts, dashboards and apps fail until users create new tokens. Reinstalling Matomo over the old database, as one of Matomo's move guides describes, writes a new config.ini.php with a new salt; copying the original file avoids that.

Terminal
tar -czf /var/backups/matomo/matomo-config-$(date +%F).tar.gz -C /var/www matomo/config/config.ini.php

This file holds the database password and the salt. Store its archive apart from the dumps, encrypt it, and archive it again whenever it changes.

Archive the Matomo directory

Terminal
tar -czf /var/backups/matomo/matomo-files-$(date +%F).tar.gz --exclude='matomo/tmp/*' --exclude='matomo/config/config.ini.php' -C /var/www matomo

That covers plugins, misc/user/, GeoIP files and the exact Matomo version. The official Docker image keeps everything under a volume at /var/www/html; see Docker volume backups.

Automate it with cron

/usr/local/bin/matomo-backup.sh
#!/bin/bash
set -euo pipefail

APP_DIR="/var/www"
APP="matomo"
DB_NAME="matomo"
BACKUP_DIR="/var/backups/matomo"
KEEP_DAYS=30
STAMP="$(date +%Y-%m-%d_%H%M)"
DB="$BACKUP_DIR/$APP-db-$STAMP.sql.gz"
FILES="$BACKUP_DIR/$APP-files-$STAMP.tar.gz"

trap 'rm -f "$DB.partial" "$FILES.partial"' EXIT

mysqldump --defaults-extra-file=/etc/mysql/matomo-backup.cnf \
  --single-transaction --quick --no-autocommit --no-tablespaces \
  "$DB_NAME" | gzip > "$DB.partial"
gunzip -c "$DB.partial" | tail -n 1 | grep -q "Dump completed"
mv "$DB.partial" "$DB"

tar -czf "$FILES.partial" --exclude="$APP/tmp/*" \
  --exclude="$APP/config/config.ini.php" -C "$APP_DIR" "$APP" || [ $? -eq 1 ]
mv "$FILES.partial" "$FILES"

find "$BACKUP_DIR" -type f \( -name "$APP-db-*" -o -name "$APP-files-*" \) -mtime +"$KEEP_DAYS" -delete
Terminal
chmod 755 /usr/local/bin/matomo-backup.sh
/etc/cron.d/matomo-backup
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

35 3 * * * root flock -n /run/lock/matomo-backup.lock /usr/local/bin/matomo-backup.sh >> /var/log/matomo-backup.log 2>&1

Pick a time your archiving cron is idle; Matomo's example runs it at minute 5 of every hour. Copy the files off the server each night, for example with rclone.

Restore on a new server

Install a web server, PHP 8 with pdo_mysql, and MySQL 8 or MariaDB. Create the database and a user with the privileges Matomo's requirements list (it also asks for the global FILE privilege, which speeds up archiving):

MySQL / MariaDB prompt
CREATE DATABASE matomo CHARACTER SET utf8mb4;
CREATE USER 'matomo'@'localhost' IDENTIFIED BY 'your-password-here';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, DROP, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON matomo.* TO 'matomo'@'localhost';

Create the option file from earlier on this server, load the dump, and put the files back:

Terminal
gunzip -c /var/backups/matomo/matomo-db-2026-10-04.sql.gz | mysql --defaults-extra-file=/etc/mysql/matomo-backup.cnf matomo
Terminal
tar -xzf /var/backups/matomo/matomo-files-2026-10-04.tar.gz -C /var/www
Terminal
tar -xzf /var/backups/matomo/matomo-config-2026-10-04.tar.gz -C /var/www
Terminal
chown -R www-data:www-data /var/www/matomo

Edit [database] in config.ini.php if the host, user or password changed; keep salt and tables_prefix. If Matomo answers on a new hostname, add it, then clear the caches:

Terminal
sudo -u www-data php /var/www/matomo/console config:set 'General.trusted_hosts[]="stats.example.net"'
Terminal
sudo -u www-data php /var/www/matomo/console core:clear-caches

A new Matomo URL also means updating the tracking code on every tracked site. Set up the archiving cron again and run it once:

Terminal
sudo -u www-data php /var/www/matomo/console core:archive --url=https://stats.example.net/

Visits tracked by the old server after the dump are not in the restore, so switch soon after the final dump. The directory archive brings back the same Matomo version, as Matomo's move FAQ asks; a newer one may run a database migration that takes minutes or hours. If reports are missing or wrong while the raw logs still exist, invalidate them and archive again:

Terminal
sudo -u www-data php /var/www/matomo/console core:invalidate-report-data --dates=2026-01-01,2026-10-04 --sites=all

Test restores and verification

A restored copy runs your scheduled email reports too. Matomo's backup FAQ suggests disabling emails on a test instance:

Terminal
sudo -u www-data php /var/www/matomo/console config:set 'General.emails_enabled=0'
Terminal
sudo -u www-data php /var/www/matomo/console diagnostics:run
Terminal
mysql --defaults-extra-file=/etc/mysql/matomo-backup.cnf matomo -e "SELECT COUNT(*), MAX(visit_last_action_time) FROM matomo_log_visit"

Compare the count and the newest visit with the live server, sign in, open yesterday's Visits Log, and call the API with an existing token to prove the salt survived. Testing restores explains how to schedule this.

GDPR: backups hold visitor data

The log tables hold IP addresses (Matomo 5 masks two bytes by default), visitor IDs, any User IDs you send, page URLs and titles, locations, and ecommerce orders. Every dump holds the same, so treat it as personal data under the GDPR:

  • Raw-data deletion only purges the live database. With logs deleted after 180 days and backups kept 30, a visit can survive about 210 days. State that in your privacy policy and set a matching retention policy.
  • GDPR Tools (Administration > Privacy) deletes a data subject from the live database only. A restore of an older backup brings those visits back: keep a log of erasure requests and run them again after any restore.
  • The UK regulator's guidance accepts erased data staying in a backup until it is overwritten on schedule, if the backup is put "beyond use" meanwhile. Encrypt the backups and use them for nothing else.

Common errors

ErrorFix
A warning that starts You are now accessing Matomo from and names the configured addressAdd the new hostname to trusted_hosts[].
Your Matomo codebase is running the old version followed by both version numbersThe dump comes from a newer Matomo. Install that version.
Your Matomo database is out-of-date, and must be upgraded before you can continue.The code is newer than the dump. Run console core:update --yes, with a backup in hand.
SQLSTATE[HY000] [1045] Access denied for userFix [database] in config.ini.php, or the user's grants.
An error naming a Matomo table and ending in doesn't existtables_prefix does not match the dump, or the import failed.
API calls with existing tokens are rejectedconfig.ini.php has a new salt. Restore the original, or create new tokens.
mysqldump: Error 1412: Table definition has changed, please retry transactionA table was created or optimized during the dump. Run it again outside archiving.
Access denied; you need (at least one of) the PROCESS privilege(s) for this operationAdd --no-tablespaces.

Frequently asked questions

Can I leave the archive tables out of a Matomo backup?
Only if raw-data deletion has never been on, so every report can be rebuilt from the log tables. Rebuilding means invalidating the reports and running core:archive, which can take hours on a busy site. Matomo recommends a full backup.
What happens if I lose config.ini.php?
Matomo can be reinstalled against the existing tables with the same table prefix, but the new file gets a new salt, so every API token stops working and users must create new ones.
How much storage do Matomo backups need?
Matomo recommends a backup host with 10 times the uncompressed database size, for daily full backups kept 30 days. Compressed dumps are much smaller, but the archive tables grow every month.
Do Matomo backups contain personal data?
Yes. They hold the same visitor data as the live database, including IP addresses (masked by default), visitor IDs and any User IDs. Encrypt them and keep them only as long as your privacy policy states.

How this was checked

Commands, limits and prices were checked against these official pages, on October 4, 2026: