Matomo is a privacy-first analytics platform available as self-hosted (On-Premise) or cloud-hosted (Matomo Cloud). Its core advantage is data ownership -- when self-hosted, all analytics data stays on your servers, making GDPR compliance straightforward because no third-party processor touches your data. However, self-hosted Matomo introduces operational complexity: you are responsible for server maintenance, database performance, archiving cron jobs, and SSL configuration. A proper implementation accounts for both the tracking configuration and the infrastructure requirements.
Why Proper Implementation Matters
Data Ownership Requires Operational Responsibility
Self-hosted Matomo gives you complete data ownership, but:
- Database growth: Matomo stores raw visit data in MySQL/MariaDB. A site with 100K monthly visits generates ~2-5GB of data per year without archiving
- Archiving performance: Matomo pre-computes reports via
core:archivecron jobs. If these fail, the dashboard shows stale data or times out - PHP memory limits: Large report generation can exceed default PHP memory limits (128MB), causing incomplete reports
- SSL requirement: Tracking requests must be HTTPS; mixed content blocks tracking on HTTPS websites
- Geolocation database: GeoIP2 databases require periodic updates for accurate location data
Cookieless Tracking Is Not Automatic
Matomo can track without cookies (a major GDPR advantage), but this requires explicit configuration:
- Default behavior uses first-party cookies (like Google Analytics)
- Cookieless mode must be enabled per-site in Matomo settings
- Cookieless tracking uses fingerprint-based visitor identification, which is less accurate
- You must decide: cookies with consent, or cookieless without consent banner
Plugin Dependencies
Matomo's functionality depends on plugins. Core analytics are free, but advanced features require premium plugins:
| Feature | Plugin | Self-Hosted Cost | Cloud Included |
|---|---|---|---|
| Ecommerce | Core | Free | Yes |
| Heatmaps | Premium | Paid license | Yes (Business+) |
| Session Recording | Premium | Paid license | Yes (Business+) |
| A/B Testing | Premium | Paid license | Yes (Business+) |
| Funnels | Premium | Paid license | Yes (Business+) |
| Custom Reports | Premium | Paid license | Yes (Business+) |
| Media Analytics | Premium | Paid license | Yes (Business+) |
Plan your plugin requirements before deployment to avoid infrastructure sizing surprises.
Pre-Implementation Planning
Access and Permissions
Matomo Instance:
- For Cloud: Sign up at
matomo.cloudand access atyoursite.matomo.cloud - For Self-Hosted: Install Matomo on your server (see infrastructure requirements below)
- Log in as Super User (first account created during installation)
Required Roles:
| Role | Permissions | Use Case |
|---|---|---|
| Super User | Full system access, plugin management | System admin |
| Admin | Site settings, user management for assigned sites | Site owner |
| Write | Tag Manager access, goal configuration | Implementation team |
| View | Report access only | Stakeholders |
Key Identifiers:
| ID | Where to Find | Format |
|---|---|---|
| Site ID (idsite) | Administration > Websites > Manage | Numeric (e.g., 1, 2, 3) |
| Tracker URL | Administration > System > General Settings | https://your-matomo.example.com/matomo.php |
| Auth Token | Personal Settings > Security > Auth Tokens | Alphanumeric string |
Self-Hosted Infrastructure Requirements
Minimum Server Specifications (up to 100K visits/month):
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| RAM | 2 GB | 4 GB |
| Storage | 20 GB | 50 GB SSD |
| PHP | 7.4+ | 8.1+ |
| MySQL/MariaDB | 5.7+ / 10.3+ | 8.0+ / 10.6+ |
| Web Server | Apache or Nginx | Nginx (better performance) |
Required PHP Extensions: mbstring, pdo, pdo_mysql, gd, curl, xml, json
Consent and Privacy Decisions
Decide your consent strategy before implementation:
Option 1: Cookies with Consent (recommended for most sites)
- Use Matomo's built-in consent management or external CMP
- Full visitor tracking accuracy with cookies
- Requires consent banner for EU/EEA visitors
Option 2: Cookieless Without Consent
- Enable
disableCookies()in tracking code - No consent banner needed (in most jurisdictions)
- Reduced accuracy: repeat visitors may be counted as new
- No cross-session user tracking
Option 3: Cookie Consent Mode
- Track without cookies by default
- Upgrade to cookie-based tracking after consent
- Best of both worlds but more complex implementation
Implementation Walkthrough
Step 1: Install Matomo Tracking Code
Standard Installation (with cookies):
<!-- Matomo Tracking Code -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://your-matomo-instance.example.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<noscript><p><img referrerpolicy="no-referrer-when-downgrade"
src="https://your-matomo-instance.example.com/matomo.php?idsite=1&rec=1"
style="border:0;" alt="" /></p></noscript>
<!-- End Matomo Tracking Code -->
Cookieless Installation (no consent required):
<script>
var _paq = window._paq = window._paq || [];
_paq.push(['disableCookies']); // No cookies set
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://your-matomo-instance.example.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
Consent Mode Installation (cookies only after consent):
<script>
var _paq = window._paq = window._paq || [];
// Require consent before tracking
_paq.push(['requireConsent']);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://your-matomo-instance.example.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<script>
// When user grants consent (called from your consent banner)
function grantMatomoConsent() {
_paq.push(['setConsentGiven']);
// Consent is remembered for subsequent visits
}
// When user revokes consent
function revokeMatomoConsent() {
_paq.push(['forgetConsentGiven']);
}
</script>
Step 2: Configure Cross-Domain Tracking
If your users navigate between multiple domains (e.g., www.example.com and shop.example.com):
// On both domains, add before trackPageView:
_paq.push(['setDomains', ['*.example.com', '*.shop.example.com']]);
_paq.push(['setCookieDomain', '.example.com']); // Share cookies across subdomains
_paq.push(['enableCrossDomainLinking']); // For different root domains
_paq.push(['trackPageView']);
Step 3: Implement Ecommerce Tracking
Product View:
// On product detail pages
_paq.push(['setEcommerceView',
'SKU-12345', // Product SKU
'Premium Widget', // Product Name
'Widgets', // Product Category (or array for nested categories)
49.99 // Product Price
]);
_paq.push(['trackPageView']);
Cart Update:
// When cart changes (add, remove, update quantity)
// Add each item in the cart:
_paq.push(['addEcommerceItem',
'SKU-12345', // Product SKU (required)
'Premium Widget', // Product Name
'Widgets', // Category
49.99, // Unit Price
2 // Quantity
]);
_paq.push(['addEcommerceItem',
'SKU-67890',
'Deluxe Gadget',
'Gadgets',
79.99,
1
]);
// Track the cart update with total value
_paq.push(['trackEcommerceCartUpdate', 179.97]);
Purchase:
// On order confirmation page
// First, add all purchased items:
_paq.push(['addEcommerceItem', 'SKU-12345', 'Premium Widget', 'Widgets', 49.99, 2]);
_paq.push(['addEcommerceItem', 'SKU-67890', 'Deluxe Gadget', 'Gadgets', 79.99, 1]);
// Then track the order:
_paq.push(['trackEcommerceOrder',
'ORD-2024-12345', // Order ID (required, unique)
179.97, // Grand Total (required)
159.97, // Subtotal (optional)
12.80, // Tax (optional)
7.20, // Shipping (optional)
false // Discount applied (optional, boolean)
]);
Step 4: Set Up Custom Dimensions
Custom dimensions extend the default tracking with business-specific data:
- In Matomo Admin, go to Custom Dimensions (under Manage)
- Create dimensions:
| Scope | Name | Example Values |
|---|---|---|
| Visit | User Type | free, pro, enterprise |
| Visit | Logged In | yes, no |
| Action | Page Type | product, category, blog, checkout |
| Action | Author | author name (for content sites) |
- Implement in tracking code:
// Visit-scoped dimensions (set once per visit)
_paq.push(['setCustomDimension', 1, 'pro']); // dimension1 = User Type
_paq.push(['setCustomDimension', 2, 'yes']); // dimension2 = Logged In
// Action-scoped dimensions (set per page/event)
_paq.push(['setCustomDimension', 3, 'product']); // dimension3 = Page Type
_paq.push(['setCustomDimension', 4, 'John Smith']); // dimension4 = Author
_paq.push(['trackPageView']);
Step 5: Configure Goals and Events
Goals (track conversions):
- In Matomo, go to Administration > Goals
- Create goals:
| Goal | Type | Match |
|---|---|---|
| Purchase Completed | Visit URL | URL contains /order-confirmation |
| Newsletter Signup | Event | Category = "newsletter", Action = "signup" |
| Contact Form Submit | Event | Category = "form", Action = "submit" |
Events (track interactions):
// Event tracking: Category, Action, Name (optional), Value (optional)
_paq.push(['trackEvent', 'Form', 'Submit', 'Contact Form']);
_paq.push(['trackEvent', 'Video', 'Play', 'Product Demo', 120]); // 120 seconds
_paq.push(['trackEvent', 'Download', 'Click', 'Whitepaper.pdf']);
_paq.push(['trackEvent', 'Newsletter', 'Signup', 'Footer Form']);
Step 6: Self-Hosted Administration
Configure Archiving Cron Job (critical for self-hosted):
# Add to crontab (run every hour for sites under 500K visits/month)
# For higher traffic, consider running every 30 minutes
5 * * * * /usr/bin/php /var/www/matomo/console core:archive --url=https://your-matomo.example.com/ > /var/log/matomo-archive.log 2>&1
Disable Browser Archiving (after setting up cron):
In Matomo Admin > System > General Settings:
- Set "Archive reports when viewed from the browser" to No
- This forces all archiving through the cron job, improving dashboard performance
GeoIP2 Database Setup:
# Download MaxMind GeoIP2 database (requires free MaxMind account)
cd /var/www/matomo/misc/
wget "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=YOUR_KEY&suffix=tar.gz" -O GeoLite2-City.tar.gz
tar xzf GeoLite2-City.tar.gz
mv GeoLite2-City_*/GeoLite2-City.mmdb .
# Set up monthly auto-update via cron
0 3 1 * * cd /var/www/matomo/misc && wget -q "https://download.maxmind.com/..." -O GeoLite2-City.tar.gz && tar xzf GeoLite2-City.tar.gz && mv GeoLite2-City_*/GeoLite2-City.mmdb .
In Matomo Admin > System > Geolocation, select "GeoIP 2 (PHP)" as the provider.
Verification and QA
Matomo Tag Manager Preview
If using Matomo Tag Manager:
- Go to Tag Manager > Container > Preview/Debug
- Enable preview mode
- Navigate your website
- Check that tags fire correctly on expected pages
Real-Time Dashboard
- In Matomo, go to Visitors > Real-time Map or Real-time Visits
- Open your website in another tab
- Navigate through several pages
- Verify:
- Visits appear in real-time
- Page titles and URLs are correct
- Custom dimensions display expected values
- Geolocation is accurate
Network Tab Verification
- Open Chrome DevTools > Network tab
- Filter for
matomo.php - Navigate your site
- For each tracking request, verify:
idsitematches your site IDaction_nameshows correct page titlee_c,e_a,e_ncontain event data (if events fire)- Custom dimension parameters (
dimension1, etc.) are present
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| No data in dashboard | Wrong Site ID or tracker URL | Verify setSiteId and setTrackerUrl in tracking code |
| Mixed content blocking | Matomo on HTTP, site on HTTPS | Configure Matomo with SSL certificate |
| Stale reports | Archiving cron not running | Check cron job, review /var/log/matomo-archive.log |
| Ecommerce not tracking | Ecommerce plugin not enabled | Admin > Websites > Manage > Enable Ecommerce |
| Cookie consent blocking | requireConsent called but consent never granted |
Verify consent banner calls setConsentGiven |
| Slow dashboard | Large database, no archiving | Enable cron archiving, disable browser archiving |
| Geolocation shows "Unknown" | GeoIP2 database not installed | Download and configure GeoLite2 database |
Deployment Artifacts
- Site ID and Tracker URL: Per environment, central reference
- Tracking code: Implementation per consent strategy (cookies, cookieless, consent mode)
- Custom dimensions registry: Dimension IDs, names, scopes, and descriptions
- Goal definitions: Names, match conditions, and revenue values
- Ecommerce schema: Product field mapping (SKU, name, category, price)
- Self-hosted configuration: Server specs, PHP settings, cron jobs, GeoIP setup
- Plugin inventory: Installed plugins, license status, and update schedule
- Consent implementation: CMP integration, consent/revoke flow documentation
Linked Runbooks
- Install or Embed the Tag or SDK -- Tracking code deployment methods
- Event Tracking -- Event and goal configuration
- Data Layer Setup -- Data layer for custom dimensions and ecommerce
- Cross-Domain Tracking -- Multi-domain cookie and linker configuration
- Server-Side vs Client-Side -- Matomo Log Analytics vs. JavaScript tracking
Change Log and Owners
- Document who administers the Matomo instance (server admin for self-hosted)
- Track plugin installations and license renewals
- Maintain custom dimension allocations with owners
- Record consent strategy changes with compliance review dates
- Review quarterly: database size, archiving performance, GeoIP database freshness