Self-hosting Matomo gives you complete control over your analytics data, infrastructure, and costs. While managed solutions like Matomo Cloud are convenient, self-hosting is the best option for organizations with strict data sovereignty requirements, GDPR compliance needs, or those who want to avoid ongoing subscription costs.
This guide covers everything you need to deploy, configure, secure, and maintain a self-hosted Matomo installation.
Estimated reading time: 35-40 minutes
System Requirements
Before deploying Matomo, ensure your infrastructure meets these requirements.
Minimum Requirements
- Web Server: Apache, Nginx (recommended), IIS, or LiteSpeed
- PHP: 7.2.5 or higher (Matomo 4.x/5.x); PHP 8.x recommended for best performance
- Database: MySQL 5.5+ or MariaDB 10.4+; MySQL 8+ or latest MariaDB recommended
- PHP Extensions: pdo, pdo_mysql (or mysqli), mbstring, gd, xml, curl, opcache
- Operating System: Linux (Ubuntu, CentOS, Debian), Windows, macOS Server, or FreeBSD
Recommended Server Sizing
Server requirements scale with your traffic volume. Below are Matomo's official recommendations:
Up to 100,000 page views/month
- Single server for both app and database
- 2 CPU cores, 2 GB RAM, 50 GB SSD
Up to 1 million page views/month
- Single server can suffice
- 4 CPU cores, 8 GB RAM, 250 GB SSD
Up to 10 million page views/month
- Separate app and database servers recommended
- App server: 8 CPUs, 16 GB RAM, 100 GB SSD (or 2× app servers with 4 CPUs, 4 GB RAM each)
- Database server: 8 CPUs, 16 GB RAM, 400 GB SSD
Up to 100 million page views/month
- Minimum 3 servers recommended
- 2-3× App servers: 16 CPUs, 16+ GB RAM, 100 GB SSD each
- 1-2× Database servers: 16 CPUs, 32 GB RAM, 1 TB SSD (with replication)
- Load balancer and CDN recommended
Over 100 million page views/month
- Minimum 5 servers
- 3+ App servers: 16 CPUs, 16+ GB RAM each
- 2× Database servers with replication: 16 CPUs, 32 GB RAM, 1 TB SSD each
- Load balancer and CDN required
- Consider contacting Matomo for enterprise architecture guidance
Deployment Options
Matomo can be deployed in several ways, each with different trade-offs for complexity, scalability, and maintenance.
Docker Deployment
Docker is the recommended approach for most self-hosted deployments. It simplifies installation, updates, and provides isolation from the host system.
Basic Docker Compose Setup
# docker-compose.yml
services:
matomo:
image: matomo:5-apache
restart: unless-stopped
ports:
- "8080:80"
volumes:
- matomo-data:/var/www/html
- ./config:/var/www/html/config
environment:
- MATOMO_DATABASE_HOST=db
- MATOMO_DATABASE_ADAPTER=mysql
- MATOMO_DATABASE_TABLES_PREFIX=matomo_
- MATOMO_DATABASE_USERNAME=matomo
- MATOMO_DATABASE_PASSWORD=${DB_PASSWORD}
- MATOMO_DATABASE_DBNAME=matomo
depends_on:
db:
condition: service_healthy
db:
image: mariadb:11
restart: unless-stopped
volumes:
- db-data:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MARIADB_DATABASE=matomo
- MARIADB_USER=matomo
- MARIADB_PASSWORD=${DB_PASSWORD}
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
volumes:
matomo-data:
db-data:
Create a .env file with your credentials:
DB_PASSWORD=your-secure-password-here
DB_ROOT_PASSWORD=your-root-password-here
Production Docker Setup with Nginx Reverse Proxy
# docker-compose.prod.yml
services:
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
- matomo-data:/var/www/html:ro
depends_on:
- matomo
matomo:
image: matomo:5-fpm-alpine
restart: unless-stopped
volumes:
- matomo-data:/var/www/html
- ./config:/var/www/html/config
environment:
- MATOMO_DATABASE_HOST=db
- MATOMO_DATABASE_ADAPTER=mysql
- MATOMO_DATABASE_TABLES_PREFIX=matomo_
- MATOMO_DATABASE_USERNAME=matomo
- MATOMO_DATABASE_PASSWORD=${DB_PASSWORD}
- MATOMO_DATABASE_DBNAME=matomo
depends_on:
db:
condition: service_healthy
db:
image: mariadb:11
restart: unless-stopped
volumes:
- db-data:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MARIADB_DATABASE=matomo
- MARIADB_USER=matomo
- MARIADB_PASSWORD=${DB_PASSWORD}
command: >
--max_allowed_packet=64MB
--innodb_buffer_pool_size=1G
--innodb_log_file_size=256M
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis-data:/data
command: redis-server --appendonly yes
volumes:
matomo-data:
db-data:
redis-data:
Kubernetes Deployment
For larger deployments or organizations already using Kubernetes, Matomo can be deployed as a scalable, highly available service.
Kubernetes Deployment Manifest
# matomo-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: matomo
labels:
app: matomo
spec:
replicas: 2
selector:
matchLabels:
app: matomo
template:
metadata:
labels:
app: matomo
spec:
containers:
- name: matomo
image: matomo:5-apache
ports:
- containerPort: 80
env:
- name: MATOMO_DATABASE_HOST
value: "mysql-service"
- name: MATOMO_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: matomo-secrets
key: db-password
- name: MATOMO_DATABASE_USERNAME
value: "matomo"
- name: MATOMO_DATABASE_DBNAME
value: "matomo"
volumeMounts:
- name: matomo-data
mountPath: /var/www/html
- name: matomo-config
mountPath: /var/www/html/config
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2000m"
livenessProbe:
httpGet:
path: /matomo.php
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /matomo.php
port: 80
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: matomo-data
persistentVolumeClaim:
claimName: matomo-pvc
- name: matomo-config
persistentVolumeClaim:
claimName: matomo-config-pvc
---
apiVersion: v1
kind: Service
metadata:
name: matomo-service
spec:
selector:
app: matomo
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: matomo-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: matomo
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Kubernetes ConfigMap and Secrets
# matomo-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: matomo-secrets
type: Opaque
stringData:
db-password: "your-secure-password"
db-root-password: "your-root-password"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: matomo-config
data:
MATOMO_DATABASE_ADAPTER: "mysql"
MATOMO_DATABASE_TABLES_PREFIX: "matomo_"
Bare Metal Installation
For traditional server deployments without containerization.
Installation Steps
# Install required packages (Ubuntu/Debian)
sudo apt update
sudo apt install -y php php-cli php-fpm php-mysql php-curl php-gd \
php-mbstring php-xml php-zip nginx mariadb-server
# Download and extract Matomo
cd /var/www
wget https://builds.matomo.org/matomo.zip
unzip matomo.zip
chown -R www-data:www-data matomo
# Secure MariaDB
sudo mysql_secure_installation
# Create database and user
sudo mysql -e "CREATE DATABASE matomo;"
sudo mysql -e "CREATE USER 'matomo'@'localhost' IDENTIFIED BY 'your-password';"
sudo mysql -e "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, DROP, ALTER, \
CREATE TEMPORARY TABLES, LOCK TABLES ON matomo.* TO 'matomo'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"
PHP Configuration
# /etc/php/8.3/fpm/conf.d/matomo.ini
memory_limit = 512M
max_execution_time = 300
upload_max_filesize = 64M
post_max_size = 64M
; Required extensions (ensure these are enabled)
extension=pdo_mysql
extension=mbstring
extension=gd
extension=xml
extension=curl
; OPcache settings for performance
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
Nginx Configuration
# /etc/nginx/sites-available/matomo
server {
listen 80;
server_name analytics.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name analytics.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/analytics.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/analytics.yourdomain.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
root /var/www/matomo;
index index.php;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
# Block access to sensitive files and directories
location ~ /\.(?!well-known) {
deny all;
}
location ~* ^/(?:README|LICENSE|CHANGELOG|CONTRIBUTING)(?:\.md|\.txt)?$ {
deny all;
}
location ~* ^/(?:config|tmp|core|lang|vendor)/ {
deny all;
}
location ~* ^/(?:\.git|\.github|\.editorconfig|\.gitignore|\.gitattributes) {
deny all;
}
# PHP handling
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
fastcgi_param HTTPS on;
}
# Matomo tracking endpoint optimization
location = /matomo.php {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_param HTTPS on;
}
location = /matomo.js {
expires 1w;
add_header Cache-Control "public, immutable";
}
# Static files caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1M;
add_header Cache-Control "public, immutable";
}
location / {
try_files $uri $uri/ =404;
}
}
Database Configuration
Proper database configuration is critical for Matomo performance.
MySQL/MariaDB Optimization
# /etc/mysql/mariadb.conf.d/99-matomo.cnf
[mysqld]
# InnoDB settings - adjust innodb_buffer_pool_size to 70-80% of available RAM
innodb_buffer_pool_size = 2G
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_buffer_pool_instances = 4
# Connection settings
max_connections = 200
wait_timeout = 300
interactive_timeout = 300
# Temporary tables
tmp_table_size = 256M
max_heap_table_size = 256M
# Packet size for large queries
max_allowed_packet = 64M
# Query execution
join_buffer_size = 4M
sort_buffer_size = 4M
read_buffer_size = 2M
read_rnd_buffer_size = 2M
# Logging (for production, minimize logging)
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
Database Maintenance
#!/bin/bash
# /usr/local/bin/matomo-db-maintenance.sh
# Weekly optimization script
MATOMO_DB="matomo"
DB_USER="matomo"
DB_PASS="your-password"
# Optimize tables
mysqlcheck -o -u "$DB_USER" -p"$DB_PASS" "$MATOMO_DB"
# Analyze key tables for query optimization
mysql -u "$DB_USER" -p"$DB_PASS" "$MATOMO_DB" -e "
ANALYZE TABLE matomo_log_visit;
ANALYZE TABLE matomo_log_link_visit_action;
ANALYZE TABLE matomo_log_action;
ANALYZE TABLE matomo_archive_numeric_2024_01;
"
Security Best Practices
Securing your Matomo installation is essential, especially when handling user analytics data.
Core Security Configuration
# config/config.ini.php additions
[General]
; Force HTTPS for all connections
force_ssl = 1
; Trusted hosts - prevent host header attacks
; Add all hostnames that will access Matomo UI
trusted_hosts[] = "analytics.yourdomain.com"
; Disable installer after setup (critical!)
enable_installer = 0
; Disable automatic updates (update manually for better control)
enable_auto_update = 0
; Require secure cookies
secure_cookie = 1
; Session security
login_cookie_expire = 1209600
; Brute force protection
login_allowlist_apply_to_reporting_api_via_token_auth = 1
[Tracker]
; Rate limiting for tracking requests
tracker_cache_file_ttl = 300
; Disable debug mode in production
debug = 0
debug_on_demand = 0
File Permissions
#!/bin/bash
# Secure Matomo file permissions
MATOMO_PATH="/var/www/matomo"
WEB_USER="www-data"
WEB_GROUP="www-data"
# Set ownership
chown -R "$WEB_USER:$WEB_GROUP" "$MATOMO_PATH"
# Secure file permissions (644 for files, 755 for directories)
find "$MATOMO_PATH" -type f -exec chmod 644 {} \;
find "$MATOMO_PATH" -type d -exec chmod 755 {} \;
# Protect sensitive files
chmod 600 "$MATOMO_PATH/config/config.ini.php"
# Required writable directories
chmod 755 "$MATOMO_PATH/tmp"
chmod 755 "$MATOMO_PATH/tmp/assets"
chmod 755 "$MATOMO_PATH/tmp/cache"
chmod 755 "$MATOMO_PATH/tmp/logs"
chmod 755 "$MATOMO_PATH/tmp/tcpdf"
chmod 755 "$MATOMO_PATH/tmp/templates_c"
# Make misc/user writable for GeoIP databases
chmod 755 "$MATOMO_PATH/misc/user"
Firewall Configuration
# UFW rules for Matomo server
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Allow MySQL only from app servers (if separate DB server)
sudo ufw allow from 10.0.0.0/8 to any port 3306
sudo ufw enable
Fail2ban Configuration
# /etc/fail2ban/filter.d/matomo.conf
[Definition]
failregex = ^<HOST> .* "POST /index.php\?module=Login.*" 403
^<HOST> .* "POST /index.php\?module=Login.*" 401
ignoreregex =
# /etc/fail2ban/jail.d/matomo.conf
[matomo]
enabled = true
port = http,https
filter = matomo
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime = 3600
Performance Optimization
Auto-Archiving Setup
For sites with more than a few hundred daily visits, disable browser-triggered archiving and use cron-based archiving instead.
# config/config.ini.php
[General]
; Disable browser-triggered archiving
browser_archiving_disabled_enforce = 1
# /etc/cron.d/matomo-archive
# Run archiving every hour (adjust based on your traffic)
MAILTO="admin@yourdomain.com"
5 * * * * www-data /usr/bin/php /var/www/matomo/console core:archive \
--url=https://analytics.yourdomain.com/ >> /var/log/matomo/archive.log 2>&1
# For high-traffic sites, you may need less frequent archiving
# 5 */2 * * * www-data /usr/bin/php /var/www/matomo/console core:archive ...
QueuedTracking Plugin for High Traffic
For high-traffic sites, use the QueuedTracking plugin to handle traffic spikes. It queues tracking requests in Redis or MySQL for asynchronous processing.
# Install the plugin via console
php /var/www/matomo/console plugin:activate QueuedTracking
Configure via the Matomo UI (Administration → System → General Settings → QueuedTracking) or via config:
# config/config.ini.php
[QueuedTracking]
; Backend: 'redis' (recommended) or 'mysql'
backend = "redis"
; Redis configuration
redisHost = "127.0.0.1"
redisPort = 6379
redisDatabase = 0
redisPassword = ""
; Number of parallel workers (set to number of available CPUs)
numQueueWorkers = 4
; Number of requests to process per batch
numRequestsToProcess = 25
; Process queue during tracking requests (disable for cron-only processing)
processDuringTrackingRequest = 0
# Process queue via cron (if processDuringTrackingRequest = 0)
# /etc/cron.d/matomo-queue
* * * * * www-data /usr/bin/php /var/www/matomo/console queuedtracking:process >> /var/log/matomo/queue.log 2>&1
Redis Caching
Enable Redis for caching to improve overall performance:
# config/config.ini.php
[Cache]
backend = redis
[RedisCache]
host = "127.0.0.1"
port = 6379
database = 1
timeout = 0.0
password = ""
GeoIP Location Database
For accurate visitor geolocation, configure MaxMind's GeoIP databases.
Automatic GeoIP Updates
# Install geoipupdate
sudo apt install geoipupdate
# Create MaxMind account at https://www.maxmind.com/en/geolite2/signup
# Then configure /etc/GeoIP.conf
AccountID YOUR_ACCOUNT_ID
LicenseKey YOUR_LICENSE_KEY
EditionIDs GeoLite2-City GeoLite2-Country
# Download databases
sudo geoipupdate
# Copy to Matomo
cp /var/lib/GeoIP/GeoLite2-City.mmdb /var/www/matomo/misc/
chown www-data:www-data /var/www/matomo/misc/GeoLite2-City.mmdb
# Setup weekly updates
echo "0 3 * * 0 root /usr/bin/geoipupdate && cp /var/lib/GeoIP/GeoLite2-City.mmdb /var/www/matomo/misc/" | sudo tee /etc/cron.d/geoip-update
Then enable in Matomo: Administration → System → Geolocation → Select "GeoIP 2 (Php)"
Scaling Matomo
As your traffic grows, you'll need to scale your Matomo infrastructure.
Horizontal Scaling Architecture
┌─────────────────────┐
│ Load Balancer │
│ (HAProxy/Nginx) │
└──────────┬──────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
┌───────▼───────┐ ┌──────────▼─────────┐ ┌───────▼───────┐
│ Matomo 1 │ │ Matomo 2 │ │ Matomo 3 │
│ (Web/API) │ │ (Web/API) │ │ (Web/API) │
└───────┬───────┘ └──────────┬─────────┘ └───────┬───────┘
│ │ │
└──────────────────────┼─────────────────────┘
│
┌──────────▼──────────┐
│ Redis Cluster │
│ (Queue + Cache) │
└──────────┬──────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
┌───────▼───────┐ ┌──────────▼─────────┐ ┌───────▼───────┐
│ MySQL Primary │ │ MySQL Replica 1 │ │ MySQL Replica │
│ (Writes) │ │ (Reads) │ │ 2 (Reads) │
└───────────────┘ └────────────────────┘ └───────────────┘
Database Read Replicas
# config/config.ini.php - Configure read replicas
[database]
host = "mysql-primary"
username = "matomo"
password = "your-password"
dbname = "matomo"
tables_prefix = "matomo_"
[database_reader]
host = "mysql-replica"
username = "matomo_reader"
password = "reader-password"
dbname = "matomo"
Maintenance and Monitoring
Automated Maintenance Script
#!/bin/bash
# /usr/local/bin/matomo-maintenance.sh
MATOMO_PATH="/var/www/matomo"
LOG_FILE="/var/log/matomo/maintenance.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
log() {
echo "[$DATE] $1" >> "$LOG_FILE"
}
log "Starting Matomo maintenance"
# Clear temporary files and caches
php "$MATOMO_PATH/console" core:clear-caches >> "$LOG_FILE" 2>&1
# Optimize archive tables
php "$MATOMO_PATH/console" database:optimize-archive-tables >> "$LOG_FILE" 2>&1
# Purge old archive data (based on retention settings)
php "$MATOMO_PATH/console" core:purge-old-archive-data >> "$LOG_FILE" 2>&1
# Delete old visitor logs (respects data retention settings)
php "$MATOMO_PATH/console" core:delete-logs-data >> "$LOG_FILE" 2>&1
# Run system diagnostics
php "$MATOMO_PATH/console" diagnostics:run >> "$LOG_FILE" 2>&1
# Check for updates (notification only)
php "$MATOMO_PATH/console" core:update --dry-run >> "$LOG_FILE" 2>&1
log "Maintenance completed"
Health Check Script
<?php
// /var/www/matomo/healthcheck.php
// Simple health check for load balancers
define('PIWIK_DOCUMENT_ROOT', __DIR__);
require_once PIWIK_DOCUMENT_ROOT . '/core/bootstrap.php';
try {
$db = \Piwik\Db::get();
$result = $db->fetchOne("SELECT 1");
if ($result === "1") {
http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['status' => 'healthy', 'database' => 'connected']);
} else {
throw new Exception('Database check failed');
}
} catch (Exception $e) {
http_response_code(503);
header('Content-Type: application/json');
echo json_encode(['status' => 'unhealthy', 'error' => $e->getMessage()]);
}
Monitoring Checklist
Key metrics to monitor for a healthy Matomo installation:
- Application: Response time for /matomo.php, error rates, queue size (if using QueuedTracking)
- Database: Connection count, query latency, replication lag, disk usage
- System: CPU utilization, memory usage, disk I/O, network throughput
- Archives: Archive processing duration, failed archives count
Backup Strategy
#!/bin/bash
# /usr/local/bin/matomo-backup.sh
BACKUP_DIR="/backup/matomo"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Database backup with compression
mysqldump -u matomo -p'your-password' \
--single-transaction \
--quick \
--lock-tables=false \
matomo | gzip > "$BACKUP_DIR/db-$DATE.sql.gz"
# Configuration backup
tar -czf "$BACKUP_DIR/config-$DATE.tar.gz" \
/var/www/matomo/config/config.ini.php \
/var/www/matomo/misc/user/
# Verify backup integrity
if gzip -t "$BACKUP_DIR/db-$DATE.sql.gz"; then
echo "Database backup verified successfully"
else
echo "ERROR: Database backup verification failed" >&2
exit 1
fi
# Remove old backups
find "$BACKUP_DIR" -type f -mtime +$RETENTION_DAYS -delete
# Optional: Upload to remote storage (S3, GCS, etc.)
# aws s3 sync "$BACKUP_DIR" s3://your-bucket/matomo-backups/ --delete
Data Privacy and GDPR Compliance
Matomo provides built-in privacy features for GDPR compliance.
Data Retention Configuration
# config/config.ini.php
[PrivacyManager]
; Automatically delete old raw data (in days, 0 = never)
delete_logs_enable = 1
delete_logs_older_than = 180
; Automatically delete old aggregated reports
delete_reports_enable = 1
delete_reports_older_than = 365
; Anonymize visitor IP addresses
ip_address_mask_length = 2
use_anonymized_ip_for_visit_enrichment = 1
Privacy Settings Checklist
- Enable "Anonymize Visitors' IP addresses" in Privacy settings
- Configure data retention periods appropriate for your jurisdiction
- Enable "Support Do Not Track preference" if required
- Use the GDPR Tools feature for data subject access requests
- Consider enabling "Require consent before tracking" for EU visitors
Upgrade Procedures
Standard Upgrade Process
#!/bin/bash
# /usr/local/bin/matomo-upgrade.sh
MATOMO_PATH="/var/www/matomo"
BACKUP_DIR="/backup/matomo-upgrades"
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup before upgrade
mkdir -p "$BACKUP_DIR"
tar -czf "$BACKUP_DIR/pre-upgrade-$DATE.tar.gz" "$MATOMO_PATH"
mysqldump -u matomo -p matomo | gzip > "$BACKUP_DIR/db-pre-upgrade-$DATE.sql.gz"
# Enable maintenance mode
touch "$MATOMO_PATH/tmp/maintenance.flag"
# Run upgrade
php "$MATOMO_PATH/console" core:update --yes
# Clear all caches
php "$MATOMO_PATH/console" core:clear-caches
# Remove maintenance mode
rm -f "$MATOMO_PATH/tmp/maintenance.flag"
# Verify upgrade
php "$MATOMO_PATH/console" diagnostics:run
echo "Upgrade completed. Please verify Matomo is working correctly."
Troubleshooting Common Issues
Tracking Not Working
- Check browser JavaScript console for errors
- Verify matomo.php is accessible:
curl -I https://analytics.yourdomain.com/matomo.php - Check trusted_hosts configuration matches your domain
- Review web server logs for 403/404/500 errors
- Ensure tracking code has correct site ID and URL
Slow Reports
- Enable cron-based archiving (disable browser archiving)
- Increase MySQL/MariaDB buffer pool size
- Enable Redis caching
- Consider QueuedTracking plugin for high traffic
- Review and disable unused plugins
High Memory Usage
- Optimize PHP memory_limit (512M is usually sufficient)
- Enable OPcache with appropriate settings
- Tune MySQL innodb_buffer_pool_size
- Enable Redis for session and cache storage
- Audit and disable unused plugins
Database Growth
- Configure data retention to automatically purge old data
- Run
database:optimize-archive-tablesregularly - Monitor table sizes: largest tables are typically log_visit and log_link_visit_action
- Consider partitioning for very large installations
Self-hosting Matomo requires ongoing attention to security, performance, and maintenance. However, the benefits of complete data ownership, privacy compliance, and cost control make it worthwhile for many organizations. Start with a simple deployment and scale as your needs grow.