Why Matomo, not GA4
If you skipped GA4 deliberately, Matomo is the closest UI/concept analog. Sessions are sessions, goals are goals, custom dimensions are custom dimensions. The mental-model swap is minimal compared to GA4’s event-only world.
If you skipped GA4 reluctantly, Matomo is also the lowest-friction destination — your team’s GA Universal muscle memory mostly carries over.
Pre-migration checklist
- Decide deployment. Matomo Cloud (€19+/mo, EU-hosted, managed) vs self-host (PHP + MySQL + 2GB RAM, ongoing patching).
- Inventory UA goals + custom dimensions. Matomo replicates ~95% of UA’s data model.
- Plan e-commerce. Matomo has native cart/checkout/refund tracking via `_paq.push` ecommerce calls — different API from UA’s, rewrite required.
- Set retention. Matomo defaults to indefinite. Configure 26 months (or per your policy) before launch.
- Archive UA. Same urgency as any UA migration — Google has signalled “early 2026” deletion.
Step-by-step
1. Archive UA (Days 1–3)
<br />
ga-export \<br />
–property=UA-XXXXX-X \<br />
–output=/archive/ua-export.parquet \<br />
–dimensions=date,source,medium,campaign,landingPage \<br />
–metrics=sessions,users,pageviews,bounceRate,goalCompletions<br />
2. Provision Matomo (Days 3–6)
Cloud: signup, get site_id, copy tracking code. 10-minute job.
Self-host: composer create-project matomo/matomo, configure DB, run web installer, generate site_id.
<br />
<script>
var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u = "https://stats.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><br />
3. Recreate goals (Days 6–8)
Each UA goal → Matomo goal with the same trigger logic. UA destination goals → Matomo URL-match goals. UA event goals → Matomo trackEvent calls.
<br />
// UA: trackEvent(‘Account’, ‘sign_up’, ’email’)<br />
// Matomo equivalent<br />
_paq.push([‘trackEvent’, ‘Account’, ‘sign_up’, ’email’]);<br />
4. Map custom dimensions (Days 8–10)
Matomo also calls them “Custom Dimensions” — same nomenclature. Configure in admin, set scope (action vs visit), wire to your data layer.
<br />
_paq.push([‘setCustomDimension’, 1, ‘logged-in’]);<br />
_paq.push([‘trackPageView’]);<br />
5. Configure GeoIP + retention (Day 10)
Cloud handles GeoIP. Self-host: install MaxMind DB (`./console diagnostics:run`). Set retention period under Admin → Privacy.
6. Validate (Days 10–21)
UA isn’t collecting, so no live parallel run. Validate against your server logs and your archive. Use the Parallel-Run Validator to compare Matomo’s first month vs UA archive’s last month — expect double-digit deltas.
Common gotchas
- E-commerce schema is rigid — Matomo doesn’t accept arbitrary item arrays. Restructure your data layer before sending purchases.
- Reporting API rate limits — Heavy Looker Studio dashboards hit limits. Cache aggressively.
- Self-host backup discipline — Matomo’s archive tables grow fast. Daily MySQL backup + monthly archive purge required.
- Plugin compatibility — Premium plugins (Heatmaps, Form Analytics) cost extra. Verify versions before promising features.
Frequently asked
Cloud vs self-host?
Cloud if traffic <500k pageviews/mo and no ops appetite. Self-host if you need data residency, EU compliance, or have >2M pageviews/mo (Cloud climbs fast).
Can I import UA historical data into Matomo?
Partially — Matomo’s Log Analytics CLI ingests Apache/nginx access logs, useful for backfilling pageview history from raw server logs. Event-level history doesn’t migrate.
Will Looker Studio work?
Yes via Reporting API + community connector. Performance is slower than GA4’s native connector. Cache.
What about Google Ads remarketing?
Matomo doesn’t replace ad-platform audiences. Run Google Ads Customer Match or server-side conversion APIs separately.
LCP impact of Matomo's tracker?
matomo.js is ~25 kB gzipped — heavier than Plausible (~1.4 kB), lighter than GA4 (~87 kB). Async loading recommended.