Matomo
Matomo is a privacy-first web analytics platform. The HelmForge chart deploys the official Matomo Apache image with persistent application storage, MySQL integration, and a scheduled core:archive CronJob for production report processing.
Key Features
- Official
docker.io/library/matomoimage pinned to5.14.0-apache - HelmForge MySQL dependency for self-contained deployments
- External MySQL or MariaDB mode for production databases
- Persistent
/var/www/htmlstorage for application files and plugins - Automatic synchronization of image code into the persistent application volume
- CronJob-based Matomo report archiving
- Ingress, Gateway API HTTPRoute, dual-stack Service fields, NetworkPolicy, PDB, and Helm tests
- External Secrets Operator support for database credentials
extraManifestsextension point for companion resources
Matomo 5.14.0 is the current stable production release. Review the official changelog, and back up the database and persistent application volume before production rollout so the native updater can be rolled back together with its data.
Installation
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install matomo helmforge/matomo --namespace matomo --create-namespace
helm install matomo oci://ghcr.io/helmforgedev/helm/matomo --namespace matomo --create-namespace
Examples
Bundled MySQL:
mysql:
enabled: true
matomo:
siteUrl: https://analytics.example.com
External database:
mysql:
enabled: false
database:
mode: external
external:
host: mysql.database.svc.cluster.local
name: matomo
username: matomo
existingSecret: matomo-database
existingSecretPasswordKey: database-password
matomo:
siteUrl: https://analytics.example.com
trustedHost: analytics.example.com
Architecture
The chart runs the Apache variant of the official Matomo container as a Deployment and stores Matomo application files on a PVC. Database connectivity is explicit:
database.mode=autouses bundled MySQL whenmysql.enabled=truedatabase.mode=mysqlforces the HelmForge MySQL subchart pathdatabase.mode=externaluses an external MySQL or MariaDB endpoint
For production, prefer a managed or platform-operated MySQL or MariaDB service and keep Matomo application files persistent. The bundled MySQL path is useful for self-contained environments and local validation.
Upgrades
The official image stores release code under /usr/src/matomo and only initializes /var/www/html when no installation exists. Because the chart persists /var/www/html, the chart enables persistence.codeSync by default. A sync-matomo-code init container copies image-managed files into the PVC before the web container starts, while retaining PVC-only configuration, marketplace plugins, and user content.
The sync preserves metadata on directories that already exist in the volume and does not archive the source directory entry itself. Images running as a non-root UID can therefore synchronize code when podSecurityContext.fsGroup makes the volume writable, without needing permission to change the mode or timestamps of the volume root.
The Deployment defaults to updateStrategy.type=Recreate, stopping the old web pod before the new init container changes files on the shared volume. Plan a brief maintenance window for image upgrades. Override this only when no serving pod shares application files with the synchronizing pod.
Back up the database and application PVC before changing image.tag. After the new web pod starts, apply required database migrations during a maintenance window:
kubectl exec -n matomo deploy/matomo -- php /var/www/html/console core:update --yes --no-interaction
Set persistence.codeSync.enabled=false only when another process manages the application code in the PVC.
Production Values
mysql:
enabled: false
database:
mode: external
external:
host: mysql-primary.database.svc.cluster.local
port: 3306
name: matomo
username: matomo
existingSecret: matomo-database
existingSecretPasswordKey: database-password
matomo:
siteUrl: https://analytics.example.com
trustedHost: analytics.example.com
persistence:
enabled: true
size: 50Gi
archiver:
enabled: true
schedule: '5 * * * *'
ingress:
enabled: true
ingressClassName: nginx
hosts:
- host: analytics.example.com
paths:
- path: /
pathType: Prefix
networkPolicy:
enabled: true
egress:
enabled: true
extraEgress:
- to:
- ipBlock:
cidr: 10.80.0.0/16
ports:
- protocol: TCP
port: 3306
Archiver
Matomo recommends scheduled archive processing for production sites. The chart renders a Kubernetes CronJob when archiver.enabled=true:
archiver:
enabled: true
schedule: '5 * * * *'
concurrencyPolicy: Forbid
activeDeadlineSeconds: 7200
extraArgs: []
Set matomo.siteUrl to the public URL used by visitors. When it is empty, the chart derives an in-cluster service URL for the archiver.
Before the web installer creates config/config.ini.php, scheduled archive runs exit successfully without doing work. Normal core:archive execution begins after Matomo is configured.
The CronJob mounts the same application volume as the web pod. For the default ReadWriteOnce PVC, archiver.sameNodeAsMatomo=true applies required pod affinity so the archiver runs on a node that already hosts a Matomo web pod. Disable it for storage that can be mounted across nodes, or use archiver.affinity, archiver.nodeSelector, and archiver.tolerations for custom scheduling.
Backups
The chart does not render a dedicated backup job. Production backup plans must cover both runtime domains:
- MySQL or MariaDB data, either through the external database platform or the bundled MySQL release.
- The Matomo application PVC at
/var/www/html, which stores configuration, plugins, and generated assets.
Take consistent backups before chart upgrades, Matomo upgrades, and plugin changes. For external databases, use the database platform’s native backup and restore process and validate restore into a separate namespace.
External Secrets
Use External Secrets Operator to project the database password into the Secret consumed by the Deployment:
mysql:
enabled: false
database:
mode: external
external:
host: mysql.database.svc.cluster.local
username: matomo
existingSecret: matomo-database
existingSecretPasswordKey: database-password
externalSecrets:
enabled: true
items:
- name: matomo-database
secretStoreRef:
name: cluster-secrets
kind: ClusterSecretStore
target:
name: matomo-database
data:
- secretKey: database-password
remoteRef:
key: production/matomo
property: database-password
Networking
Ingress:
ingress:
enabled: true
ingressClassName: nginx
hosts:
- host: analytics.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: matomo-tls
hosts:
- analytics.example.com
Gateway API:
gatewayAPI:
enabled: true
httpRoutes:
- name: public
parentRefs:
- name: public-gateway
namespace: gateway-system
hostnames:
- analytics.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
Dual-stack Service fields are available when the cluster supports them:
service:
ipFamilyPolicy: PreferDualStack
ipFamilies:
- IPv4
- IPv6
Validation
After deployment:
helm test matomo -n matomo
kubectl get pods -n matomo -l app.kubernetes.io/name=matomo
kubectl logs -n matomo deploy/matomo --since=10m
kubectl get cronjob -n matomo
Also validate the Matomo installer, tracking endpoint, archive job completion, database connectivity, and reverse proxy host configuration.
Common Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
| Installer cannot connect to database | Host, Secret key, or network policy is wrong | Check database Secret, DNS, and egress rules. |
| Reports stay empty or slow | Archiver CronJob is disabled or failing | Enable archiver.enabled and inspect CronJob logs. |
| Redirects use the wrong hostname | Public URL or trusted host is incomplete | Set matomo.siteUrl and matomo.trustedHost. |
| Plugins disappear after restart | Application persistence is disabled | Enable persistence.enabled. |
Values
| Parameter | Default | Description |
|---|---|---|
image.repository |
docker.io/library/matomo |
Official Matomo image repository. |
image.tag |
5.14.0-apache |
Official Apache image tag. |
replicaCount |
1 |
Matomo web replica count. |
matomo.siteUrl |
"" |
Public URL used by archiver jobs. |
matomo.trustedHost |
"" |
Trusted host used for redirects behind a reverse proxy. |
database.mode |
auto |
Database mode: auto, external, or mysql. |
mysql.enabled |
true |
Deploy HelmForge MySQL dependency. |
persistence.enabled |
true |
Persist /var/www/html. |
persistence.size |
10Gi |
Matomo PVC size. |
persistence.codeSync.enabled |
true |
Synchronize image code into the application PVC on startup. |
persistence.codeSync.resources |
{} |
Resources for the code synchronization init container. |
archiver.enabled |
true |
Render the core:archive CronJob. |
archiver.schedule |
5 * * * * |
Archive CronJob schedule. |
archiver.sameNodeAsMatomo |
true |
Co-locate archiver and web pods for ReadWriteOnce storage. |
archiver.nodeSelector |
{} |
Node selector for archiver pods. |
archiver.tolerations |
[] |
Tolerations for archiver pods. |
archiver.affinity |
{} |
Custom affinity that replaces generated same-node affinity. |
updateStrategy.type |
Recreate |
Deployment rollout strategy. |
ingress.enabled |
false |
Render Ingress. |
gatewayAPI.enabled |
false |
Render Gateway API HTTPRoute resources. |
externalSecrets.enabled |
false |
Render ExternalSecret resources. |
networkPolicy.enabled |
false |
Render NetworkPolicy. |
service.ipFamilyPolicy |
"" |
Kubernetes Service IP family policy. |
extraManifests |
[] |
Additional Kubernetes manifests rendered with the release. |