├── .examples ├── apache │ ├── db.env │ └── docker-compose.yml ├── helm │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ ├── ingress.yaml │ │ └── service.yaml │ └── values.yaml └── nginx │ ├── db.env │ ├── docker-compose.yml │ └── matomo.conf ├── .github ├── pull_request_template.md └── workflows │ └── ci.yml ├── Dockerfile-alpine.template ├── Dockerfile-debian.template ├── README.md ├── SECURITY.md ├── apache ├── Dockerfile ├── docker-entrypoint.sh └── php.ini ├── docker-entrypoint.sh ├── fpm-alpine ├── Dockerfile ├── docker-entrypoint.sh └── php.ini ├── fpm ├── Dockerfile ├── docker-entrypoint.sh └── php.ini ├── generate-stackbrew-library.sh ├── license.md ├── php.ini └── update.sh /.examples/apache/db.env: -------------------------------------------------------------------------------- 1 | MYSQL_PASSWORD= 2 | MYSQL_DATABASE=matomo 3 | MYSQL_USER=matomo 4 | MATOMO_DATABASE_ADAPTER=mysql 5 | MATOMO_DATABASE_TABLES_PREFIX=matomo_ 6 | MATOMO_DATABASE_USERNAME=matomo 7 | MATOMO_DATABASE_PASSWORD= 8 | MATOMO_DATABASE_DBNAME=matomo 9 | MARIADB_AUTO_UPGRADE=1 10 | MARIADB_INITDB_SKIP_TZINFO=1 11 | -------------------------------------------------------------------------------- /.examples/apache/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | db: 5 | image: mariadb:10.11 6 | command: --max-allowed-packet=64MB 7 | restart: always 8 | volumes: 9 | - db:/var/lib/mysql:Z 10 | environment: 11 | - MYSQL_ROOT_PASSWORD= 12 | - MARIADB_AUTO_UPGRADE=1 13 | - MARIADB_DISABLE_UPGRADE_BACKUP=1 14 | env_file: 15 | - ./db.env 16 | 17 | app: 18 | image: matomo 19 | restart: always 20 | volumes: 21 | # - ./config:/var/www/html/config:z 22 | # - ./logs:/var/www/html/logs:z 23 | - matomo:/var/www/html:z 24 | environment: 25 | - MATOMO_DATABASE_HOST=db 26 | env_file: 27 | - ./db.env 28 | ports: 29 | - 8080:80 30 | 31 | volumes: 32 | db: 33 | matomo: 34 | -------------------------------------------------------------------------------- /.examples/helm/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /.examples/helm/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Kubernetes 4 | name: matomo 5 | version: 0.1.0 6 | -------------------------------------------------------------------------------- /.examples/helm/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range .Values.ingress.hosts }} 4 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} 5 | {{- end }} 6 | {{- else if contains "NodePort" .Values.service.type }} 7 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "matomo.fullname" . }}) 8 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 9 | echo http://$NODE_IP:$NODE_PORT 10 | {{- else if contains "LoadBalancer" .Values.service.type }} 11 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 12 | You can watch the status of by running 'kubectl get svc -w {{ template "matomo.fullname" . }}' 13 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "matomo.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 14 | echo http://$SERVICE_IP:{{ .Values.service.port }} 15 | {{- else if contains "ClusterIP" .Values.service.type }} 16 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "matomo.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 17 | echo "Visit http://127.0.0.1:8080 to use your application" 18 | kubectl port-forward $POD_NAME 8080:80 19 | {{- end }} 20 | -------------------------------------------------------------------------------- /.examples/helm/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "matomo.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "matomo.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "matomo.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /.examples/helm/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ template "matomo.fullname" . }} 5 | labels: 6 | app: {{ template "matomo.name" . }} 7 | chart: {{ template "matomo.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | data: 11 | nginx.conf: | 12 | # nginx.conf for php 13 | events { 14 | worker_connections 768; 15 | } 16 | 17 | http { 18 | upstream backend { 19 | server localhost:9000; 20 | } 21 | 22 | include /etc/nginx/mime.types; 23 | default_type application/octet-stream; 24 | gzip on; 25 | gzip_disable "msie6"; 26 | 27 | # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the 28 | # scheme used to connect to this server 29 | map $http_x_forwarded_proto $proxy_x_forwarded_proto { 30 | default $http_x_forwarded_proto; 31 | '' $scheme; 32 | } 33 | # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the 34 | # server port the client connected to 35 | map $http_x_forwarded_port $proxy_x_forwarded_port { 36 | default $http_x_forwarded_port; 37 | '' $server_port; 38 | } 39 | # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any 40 | # Connection header that may have been passed to this server 41 | map $http_upgrade $proxy_connection { 42 | default upgrade; 43 | '' close; 44 | } 45 | # Set appropriate X-Forwarded-Ssl header 46 | map $scheme $proxy_x_forwarded_ssl { 47 | default off; 48 | https on; 49 | } 50 | # HTTP 1.1 support 51 | proxy_http_version 1.1; 52 | proxy_buffering off; 53 | proxy_set_header Host $http_host; 54 | proxy_set_header Upgrade $http_upgrade; 55 | proxy_set_header Connection $proxy_connection; 56 | proxy_set_header X-Real-IP $remote_addr; 57 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 58 | proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; 59 | proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; 60 | proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; 61 | proxy_set_header Proxy ""; 62 | 63 | server { 64 | listen 80; 65 | 66 | root /var/www/html/; 67 | index index.php index.html index.htm; 68 | 69 | location / { 70 | try_files $uri $uri/ =404; 71 | } 72 | 73 | error_page 404 /404.html; 74 | error_page 500 502 503 504 /50x.html; 75 | location = /50x.html { 76 | root /usr/share/nginx/html; 77 | } 78 | 79 | location = /favicon.ico { 80 | log_not_found off; 81 | access_log off; 82 | } 83 | 84 | location ~ \.php$ { 85 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 86 | fastcgi_param SERVER_SOFTWARE nginx; 87 | fastcgi_param QUERY_STRING $query_string; 88 | fastcgi_param REQUEST_METHOD $request_method; 89 | fastcgi_param CONTENT_TYPE $content_type; 90 | fastcgi_param CONTENT_LENGTH $content_length; 91 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 92 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 93 | fastcgi_param REQUEST_URI $request_uri; 94 | fastcgi_param DOCUMENT_URI $document_uri; 95 | fastcgi_param DOCUMENT_ROOT $document_root; 96 | fastcgi_param SERVER_PROTOCOL $server_protocol; 97 | fastcgi_param REMOTE_ADDR $remote_addr; 98 | fastcgi_param REMOTE_PORT $remote_port; 99 | fastcgi_param SERVER_ADDR $server_addr; 100 | fastcgi_param SERVER_PORT $server_port; 101 | fastcgi_param SERVER_NAME $server_name; 102 | fastcgi_intercept_errors on; 103 | fastcgi_pass backend; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /.examples/helm/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta2 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "matomo.fullname" . }} 5 | labels: 6 | app: {{ template "matomo.name" . }} 7 | chart: {{ template "matomo.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | replicas: {{ .Values.replicaCount }} 12 | selector: 13 | matchLabels: 14 | app: {{ template "matomo.name" . }} 15 | release: {{ .Release.Name }} 16 | template: 17 | metadata: 18 | labels: 19 | app: {{ template "matomo.name" . }} 20 | release: {{ .Release.Name }} 21 | spec: 22 | containers: 23 | - name: {{ .Chart.Name }}-nginx 24 | image: "{{ .Values.matomoNginx.image.repository }}:{{ .Values.matomoNginx.image.tag }}" 25 | imagePullPolicy: {{ .Values.matomoNginx.image.pullPolicy }} 26 | command: ["nginx", "-g", "daemon off;", "-c", "/config/nginx.conf"] 27 | ports: 28 | - name: http 29 | containerPort: 80 30 | protocol: TCP 31 | # livenessProbe: 32 | # httpGet: 33 | # path: / 34 | # port: http 35 | # readinessProbe: 36 | # httpGet: 37 | # path: / 38 | # port: http 39 | volumeMounts: 40 | - mountPath: /config 41 | name: config 42 | - mountPath: /var/www/html 43 | name: html-files 44 | - name: {{ .Chart.Name }} 45 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 46 | imagePullPolicy: {{ .Values.image.pullPolicy }} 47 | ports: 48 | - name: http 49 | containerPort: 9000 50 | protocol: TCP 51 | volumeMounts: 52 | - mountPath: /var/www/html 53 | name: html-files 54 | resources: 55 | {{ toYaml .Values.resources | indent 12 }} 56 | {{- with .Values.nodeSelector }} 57 | nodeSelector: 58 | {{ toYaml . | indent 8 }} 59 | {{- end }} 60 | {{- with .Values.affinity }} 61 | affinity: 62 | {{ toYaml . | indent 8 }} 63 | {{- end }} 64 | {{- with .Values.tolerations }} 65 | tolerations: 66 | {{ toYaml . | indent 8 }} 67 | {{- end }} 68 | volumes: 69 | - name: config 70 | configMap: 71 | name: {{ template "matomo.fullname" . }} 72 | - name: html-files 73 | emptyDir: {} 74 | -------------------------------------------------------------------------------- /.examples/helm/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "matomo.fullname" . -}} 3 | {{- $ingressPath := .Values.ingress.path -}} 4 | apiVersion: extensions/v1beta1 5 | kind: Ingress 6 | metadata: 7 | name: {{ $fullName }} 8 | labels: 9 | app: {{ template "matomo.name" . }} 10 | chart: {{ template "matomo.chart" . }} 11 | release: {{ .Release.Name }} 12 | heritage: {{ .Release.Service }} 13 | {{- with .Values.ingress.annotations }} 14 | annotations: 15 | {{ toYaml . | indent 4 }} 16 | {{- end }} 17 | spec: 18 | {{- if .Values.ingress.tls }} 19 | tls: 20 | {{- range .Values.ingress.tls }} 21 | - hosts: 22 | {{- range .hosts }} 23 | - {{ . }} 24 | {{- end }} 25 | secretName: {{ .secretName }} 26 | {{- end }} 27 | {{- end }} 28 | rules: 29 | {{- range .Values.ingress.hosts }} 30 | - host: {{ . }} 31 | http: 32 | paths: 33 | - path: {{ $ingressPath }} 34 | backend: 35 | serviceName: {{ $fullName }} 36 | servicePort: http 37 | {{- end }} 38 | {{- end }} 39 | -------------------------------------------------------------------------------- /.examples/helm/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ template "matomo.fullname" . }} 5 | labels: 6 | app: {{ template "matomo.name" . }} 7 | chart: {{ template "matomo.chart" . }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | type: {{ .Values.service.type }} 12 | ports: 13 | - port: {{ .Values.service.port }} 14 | targetPort: http 15 | protocol: TCP 16 | name: http 17 | selector: 18 | app: {{ template "matomo.name" . }} 19 | release: {{ .Release.Name }} 20 | -------------------------------------------------------------------------------- /.examples/helm/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for matomo. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: matomo 9 | tag: 3.5.1-fpm-alpine 10 | pullPolicy: IfNotPresent 11 | 12 | service: 13 | type: ClusterIP 14 | port: 80 15 | 16 | ingress: 17 | enabled: false 18 | annotations: {} 19 | # kubernetes.io/ingress.class: nginx 20 | # kubernetes.io/tls-acme: "true" 21 | path: / 22 | hosts: 23 | - analytics.yourdomain.com 24 | tls: [] 25 | # - secretName: chart-example-tls 26 | # hosts: 27 | # - chart-example.local 28 | 29 | resources: {} 30 | # We usually recommend not to specify default resources and to leave this as a conscious 31 | # choice for the user. This also increases chances charts run on environments with little 32 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 33 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 34 | # limits: 35 | # cpu: 100m 36 | # memory: 128Mi 37 | # requests: 38 | # cpu: 100m 39 | # memory: 128Mi 40 | 41 | nodeSelector: {} 42 | 43 | tolerations: [] 44 | 45 | affinity: {} 46 | 47 | matomoNginx: 48 | image: 49 | repository: nginx 50 | tag: alpine 51 | pullPolicy: IfNotPresent 52 | -------------------------------------------------------------------------------- /.examples/nginx/db.env: -------------------------------------------------------------------------------- 1 | MYSQL_PASSWORD= 2 | MYSQL_DATABASE=matomo 3 | MYSQL_USER=matomo 4 | MATOMO_DATABASE_ADAPTER=mysql 5 | MATOMO_DATABASE_TABLES_PREFIX=matomo_ 6 | MATOMO_DATABASE_USERNAME=matomo 7 | MATOMO_DATABASE_PASSWORD= 8 | MATOMO_DATABASE_DBNAME=matomo 9 | MARIADB_AUTO_UPGRADE=1 10 | MARIADB_INITDB_SKIP_TZINFO=1 11 | -------------------------------------------------------------------------------- /.examples/nginx/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | db: 5 | image: mariadb:10.11 6 | command: --max-allowed-packet=64MB 7 | restart: always 8 | volumes: 9 | - db:/var/lib/mysql:Z 10 | environment: 11 | - MYSQL_ROOT_PASSWORD= 12 | - MARIADB_AUTO_UPGRADE=1 13 | - MARIADB_DISABLE_UPGRADE_BACKUP=1 14 | env_file: 15 | - ./db.env 16 | 17 | app: 18 | image: matomo:fpm-alpine 19 | restart: always 20 | links: 21 | - db 22 | volumes: 23 | # - ./config:/var/www/html/config:z 24 | # - ./logs:/var/www/html/logs:z 25 | - matomo:/var/www/html:z 26 | environment: 27 | - MATOMO_DATABASE_HOST=db 28 | - PHP_MEMORY_LIMIT=2048M 29 | env_file: 30 | - ./db.env 31 | 32 | web: 33 | image: nginx:alpine 34 | restart: always 35 | volumes: 36 | - matomo:/var/www/html:z,ro 37 | # see https://github.com/matomo-org/matomo-nginx 38 | - ./matomo.conf:/etc/nginx/conf.d/default.conf:z,ro 39 | ports: 40 | - 8080:80 41 | 42 | volumes: 43 | db: 44 | matomo: 45 | -------------------------------------------------------------------------------- /.examples/nginx/matomo.conf: -------------------------------------------------------------------------------- 1 | upstream php-handler { 2 | server app:9000; 3 | } 4 | 5 | server { 6 | listen 80; 7 | 8 | add_header Referrer-Policy origin; # make sure outgoing links don't show the URL to the Matomo instance 9 | root /var/www/html; # replace with path to your matomo instance 10 | index index.php; 11 | try_files $uri $uri/ =404; 12 | 13 | ## only allow accessing the following php files 14 | location ~ ^/(index|matomo|piwik|js/index|plugins/HeatmapSessionRecording/configs).php { 15 | # regex to split $uri to $fastcgi_script_name and $fastcgi_path 16 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 17 | 18 | # Check that the PHP script exists before passing it 19 | try_files $fastcgi_script_name =404; 20 | 21 | include fastcgi_params; 22 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 23 | fastcgi_param PATH_INFO $fastcgi_path_info; 24 | fastcgi_param HTTP_PROXY ""; # prohibit httpoxy: https://httpoxy.org/ 25 | fastcgi_pass php-handler; 26 | } 27 | 28 | ## deny access to all other .php files 29 | location ~* ^.+\.php$ { 30 | deny all; 31 | return 403; 32 | } 33 | 34 | ## disable all access to the following directories 35 | location ~ /(config|tmp|core|lang) { 36 | deny all; 37 | return 403; # replace with 404 to not show these directories exist 38 | } 39 | location ~ /\.ht { 40 | deny all; 41 | return 403; 42 | } 43 | 44 | location ~ js/container_.*_preview\.js$ { 45 | expires off; 46 | add_header Cache-Control 'private, no-cache, no-store'; 47 | } 48 | 49 | location ~ \.(gif|ico|jpg|png|svg|js|css|htm|html|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2|json)$ { 50 | allow all; 51 | ## Cache images,CSS,JS and webfonts for an hour 52 | ## Increasing the duration may improve the load-time, but may cause old files to show after an Matomo upgrade 53 | expires 1h; 54 | add_header Pragma public; 55 | add_header Cache-Control "public"; 56 | } 57 | 58 | location ~ /(libs|vendor|plugins|misc/user) { 59 | deny all; 60 | return 403; 61 | } 62 | 63 | ## properly display textfiles in root directory 64 | location ~/(.*\.md|LEGALNOTICE|LICENSE) { 65 | default_type text/plain; 66 | } 67 | } 68 | 69 | # vim: filetype=nginx 70 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | - cron: 0 0 * * 0 8 | 9 | defaults: 10 | run: 11 | shell: 'bash -Eeuo pipefail -x {0}' 12 | 13 | jobs: 14 | generate-jobs: 15 | name: Generate Jobs 16 | runs-on: ubuntu-latest 17 | outputs: 18 | strategy: ${{ steps.generate-jobs.outputs.strategy }} 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: docker-library/bashbrew@v0.1.13 22 | - id: generate-jobs 23 | name: Generate Jobs 24 | run: | 25 | strategy="$(GITHUB_REPOSITORY=matomo "$BASHBREW_SCRIPTS/github-actions/generate.sh")" 26 | strategy="$("$BASHBREW_SCRIPTS/github-actions/munge-i386.sh" -c <<<"$strategy")" 27 | echo "strategy=$strategy" >> "$GITHUB_OUTPUT" 28 | jq . <<<"$strategy" # sanity check / debugging aid 29 | 30 | test: 31 | needs: generate-jobs 32 | strategy: ${{ fromJson(needs.generate-jobs.outputs.strategy) }} 33 | name: ${{ matrix.name }} 34 | runs-on: ${{ matrix.os }} 35 | steps: 36 | - uses: actions/checkout@v3 37 | - name: Prepare Environment 38 | run: ${{ matrix.runs.prepare }} 39 | - name: Pull Dependencies 40 | run: ${{ matrix.runs.pull }} 41 | - name: Build ${{ matrix.name }} 42 | run: ${{ matrix.runs.build }} 43 | - name: History ${{ matrix.name }} 44 | run: ${{ matrix.runs.history }} 45 | - name: Test ${{ matrix.name }} 46 | run: ${{ matrix.runs.test }} 47 | - name: '"docker images"' 48 | run: ${{ matrix.runs.images }} 49 | -------------------------------------------------------------------------------- /Dockerfile-alpine.template: -------------------------------------------------------------------------------- 1 | FROM php:8.3-%%VARIANT%% 2 | 3 | ENV PHP_MEMORY_LIMIT=256M 4 | 5 | RUN set -ex; \ 6 | \ 7 | apk add --no-cache --virtual .build-deps \ 8 | $PHPIZE_DEPS \ 9 | autoconf \ 10 | freetype-dev \ 11 | icu-dev \ 12 | libjpeg-turbo-dev \ 13 | libpng-dev \ 14 | libzip-dev \ 15 | openldap-dev \ 16 | pcre-dev \ 17 | procps \ 18 | ; \ 19 | \ 20 | docker-php-ext-configure gd --with-freetype --with-jpeg; \ 21 | docker-php-ext-configure ldap; \ 22 | docker-php-ext-install -j "$(nproc)" \ 23 | gd \ 24 | bcmath \ 25 | ldap \ 26 | mysqli \ 27 | opcache \ 28 | pdo_mysql \ 29 | zip \ 30 | ; \ 31 | \ 32 | # pecl will claim success even if one install fails, so we need to perform each install separately 33 | pecl install APCu-5.1.24; \ 34 | pecl install redis-6.1.0; \ 35 | \ 36 | docker-php-ext-enable \ 37 | apcu \ 38 | redis \ 39 | ; \ 40 | rm -r /tmp/pear; \ 41 | \ 42 | runDeps="$( \ 43 | scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ 44 | | tr ',' '\n' \ 45 | | sort -u \ 46 | | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ 47 | )"; \ 48 | apk add --no-network --virtual .matomo-phpext-rundeps $runDeps; \ 49 | apk del --no-network .build-deps 50 | 51 | # set recommended PHP.ini settings 52 | # see https://secure.php.net/manual/en/opcache.installation.php 53 | RUN { \ 54 | echo 'opcache.memory_consumption=128'; \ 55 | echo 'opcache.interned_strings_buffer=8'; \ 56 | echo 'opcache.max_accelerated_files=4000'; \ 57 | echo 'opcache.revalidate_freq=2'; \ 58 | echo 'opcache.fast_shutdown=1'; \ 59 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 60 | 61 | ENV MATOMO_VERSION %%VERSION%% 62 | 63 | RUN set -ex; \ 64 | apk add --no-cache --virtual .fetch-deps \ 65 | gnupg \ 66 | ; \ 67 | \ 68 | curl -fsSL -o matomo.tar.gz \ 69 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz"; \ 70 | curl -fsSL -o matomo.tar.gz.asc \ 71 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz.asc"; \ 72 | export GNUPGHOME="$(mktemp -d)"; \ 73 | gpg --batch --keyserver keyserver.ubuntu.com --recv-keys F529A27008477483777FC23D63BB30D0E5D2C749; \ 74 | gpg --batch --verify matomo.tar.gz.asc matomo.tar.gz; \ 75 | gpgconf --kill all; \ 76 | rm -rf "$GNUPGHOME" matomo.tar.gz.asc; \ 77 | tar -xzf matomo.tar.gz -C /usr/src/; \ 78 | rm matomo.tar.gz; \ 79 | apk del .fetch-deps 80 | 81 | COPY php.ini /usr/local/etc/php/conf.d/php-matomo.ini 82 | 83 | COPY docker-entrypoint.sh /entrypoint.sh 84 | 85 | # WORKDIR is /var/www/html (inherited via "FROM php") 86 | # "/entrypoint.sh" will populate it at container startup from /usr/src/matomo 87 | VOLUME /var/www/html 88 | 89 | ENTRYPOINT ["/entrypoint.sh"] 90 | CMD ["%%CMD%%"] 91 | -------------------------------------------------------------------------------- /Dockerfile-debian.template: -------------------------------------------------------------------------------- 1 | FROM php:8.3-%%VARIANT%% 2 | 3 | ENV PHP_MEMORY_LIMIT=256M 4 | 5 | RUN set -ex; \ 6 | \ 7 | savedAptMark="$(apt-mark showmanual)"; \ 8 | \ 9 | apt-get update; \ 10 | apt-get install -y --no-install-recommends \ 11 | libfreetype-dev \ 12 | libjpeg-dev \ 13 | libldap2-dev \ 14 | libpng-dev \ 15 | libzip-dev \ 16 | procps \ 17 | ; \ 18 | \ 19 | debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ 20 | docker-php-ext-configure gd --with-freetype --with-jpeg; \ 21 | docker-php-ext-configure ldap --with-libdir="lib/$debMultiarch"; \ 22 | docker-php-ext-install -j "$(nproc)" \ 23 | gd \ 24 | bcmath \ 25 | ldap \ 26 | mysqli \ 27 | opcache \ 28 | pdo_mysql \ 29 | zip \ 30 | ; \ 31 | \ 32 | # pecl will claim success even if one install fails, so we need to perform each install separately 33 | pecl install APCu-5.1.24; \ 34 | pecl install redis-6.1.0; \ 35 | \ 36 | docker-php-ext-enable \ 37 | apcu \ 38 | redis \ 39 | ; \ 40 | rm -r /tmp/pear; \ 41 | \ 42 | # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies 43 | apt-mark auto '.*' > /dev/null; \ 44 | apt-mark manual $savedAptMark; \ 45 | ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \ 46 | | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ 47 | | sort -u \ 48 | | xargs -r dpkg-query --search \ 49 | | cut -d: -f1 \ 50 | | sort -u \ 51 | | xargs -rt apt-mark manual; \ 52 | \ 53 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ 54 | rm -rf /var/lib/apt/lists/* 55 | 56 | # set recommended PHP.ini settings 57 | # see https://secure.php.net/manual/en/opcache.installation.php 58 | RUN { \ 59 | echo 'opcache.memory_consumption=128'; \ 60 | echo 'opcache.interned_strings_buffer=8'; \ 61 | echo 'opcache.max_accelerated_files=4000'; \ 62 | echo 'opcache.revalidate_freq=2'; \ 63 | echo 'opcache.fast_shutdown=1'; \ 64 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 65 | 66 | ENV MATOMO_VERSION %%VERSION%% 67 | 68 | RUN set -ex; \ 69 | fetchDeps=" \ 70 | dirmngr \ 71 | gnupg \ 72 | "; \ 73 | apt-get update; \ 74 | apt-get install -y --no-install-recommends \ 75 | $fetchDeps \ 76 | ; \ 77 | \ 78 | curl -fsSL -o matomo.tar.gz \ 79 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz"; \ 80 | curl -fsSL -o matomo.tar.gz.asc \ 81 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz.asc"; \ 82 | export GNUPGHOME="$(mktemp -d)"; \ 83 | gpg --batch --keyserver keyserver.ubuntu.com --recv-keys F529A27008477483777FC23D63BB30D0E5D2C749; \ 84 | gpg --batch --verify matomo.tar.gz.asc matomo.tar.gz; \ 85 | gpgconf --kill all; \ 86 | rm -rf "$GNUPGHOME" matomo.tar.gz.asc; \ 87 | tar -xzf matomo.tar.gz -C /usr/src/; \ 88 | rm matomo.tar.gz; \ 89 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $fetchDeps; \ 90 | rm -rf /var/lib/apt/lists/* 91 | 92 | COPY php.ini /usr/local/etc/php/conf.d/php-matomo.ini 93 | 94 | COPY docker-entrypoint.sh /entrypoint.sh 95 | 96 | # WORKDIR is /var/www/html (inherited via "FROM php") 97 | # "/entrypoint.sh" will populate it at container startup from /usr/src/matomo 98 | VOLUME /var/www/html 99 | 100 | ENTRYPOINT ["/entrypoint.sh"] 101 | CMD ["%%CMD%%"] 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Matomo (formerly Piwik) 2 | 3 | [![GitHub CI build status badge](https://github.com/matomo-org/docker/workflows/GitHub%20CI/badge.svg)](https://github.com/matomo-org/docker/actions?query=workflow%3A%22GitHub+CI%22) 4 | [![update.sh build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/update.sh/job/matomo.svg?label=Automated%20update.sh)](https://doi-janky.infosiftr.net/job/update.sh/job/matomo/) 5 | [![amd64 build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/amd64/job/matomo.svg?label=amd64)](https://doi-janky.infosiftr.net/job/multiarch/job/amd64/job/matomo) 6 | [![arm32v5 build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/arm32v5/job/matomo.svg?label=arm32v5)](https://doi-janky.infosiftr.net/job/multiarch/job/arm32v5/job/matomo) 7 | [![arm32v6 build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/arm32v6/job/matomo.svg?label=arm32v6)](https://doi-janky.infosiftr.net/job/multiarch/job/arm32v6/job/matomo) 8 | [![arm32v7 build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/arm32v7/job/matomo.svg?label=arm32v7)](https://doi-janky.infosiftr.net/job/multiarch/job/arm32v7/job/matomo) 9 | [![arm64v8 build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/arm64v8/job/matomo.svg?label=arm64v8)](https://doi-janky.infosiftr.net/job/multiarch/job/arm64v8/job/matomo) 10 | [![i386 build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/i386/job/matomo.svg?label=i386)](https://doi-janky.infosiftr.net/job/multiarch/job/i386/job/matomo) 11 | [![mips64le build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/mips64le/job/matomo.svg?label=mips64le)](https://doi-janky.infosiftr.net/job/multiarch/job/mips64le/job/matomo) 12 | [![ppc64le build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/ppc64le/job/matomo.svg?label=ppc64le)](https://doi-janky.infosiftr.net/job/multiarch/job/ppc64le/job/matomo) 13 | [![s390x build status badge](https://img.shields.io/jenkins/s/https/doi-janky.infosiftr.net/job/multiarch/job/s390x/job/matomo.svg?label=s390x)](https://doi-janky.infosiftr.net/job/multiarch/job/s390x/job/matomo) 14 | 15 | Matomo logo 16 | [Matomo](https://matomo.org/) (formerly Piwik) is the leading open-source analytics platform that gives you more than just powerful analytics: 17 | 18 | - Free open-source software 19 | - 100% data ownership 20 | - User privacy protection 21 | - User-centric insights 22 | - Customisable and extensible 23 | 24 | ## How to use this image 25 | 26 | You can run the Matomo container and service like so: 27 | 28 | ```bash 29 | docker run -d --link some-mysql:db matomo 30 | ``` 31 | 32 | This assumes you've already launched a suitable MySQL or MariaDB database container. 33 | 34 | ## Persistent data 35 | 36 | Use a Docker volume to keep persistent data: 37 | 38 | ```console 39 | docker run -d -p 8080:80 --link some-mysql:db -v matomo:/var/www/html matomo 40 | ``` 41 | 42 | ## Matomo Installation 43 | 44 | Once you're up and running, you'll arrive at the configuration wizard page. If you're using the compose file, at the `Database Setup` step, please enter the following: 45 | 46 | - Database Server: `db` 47 | - Login: MYSQL_USER 48 | - Password: MYSQL_PASSWORD 49 | - Database Name: MYSQL_DATABASE 50 | 51 | And leave the rest as default. 52 | 53 | Then you can continue the installation with the super user. 54 | 55 | The following environment variables are also honored for configuring your Matomo instance: 56 | 57 | - `MATOMO_DATABASE_HOST` 58 | - `MATOMO_DATABASE_ADAPTER` 59 | - `MATOMO_DATABASE_TABLES_PREFIX` 60 | - `MATOMO_DATABASE_USERNAME` 61 | - `MATOMO_DATABASE_PASSWORD` 62 | - `MATOMO_DATABASE_DBNAME` 63 | 64 | The PHP memory limit can be configured with the following environment variable: 65 | 66 | - `PHP_MEMORY_LIMIT` 67 | 68 | ## Docker-compose examples and log import instructions 69 | 70 | A minimal set-up using docker-compose is available in the [.examples folder](.examples/nginx/docker-compose.yml), a more complete [example can be found at IndieHosters/piwik](https://github.com/libresh/compose-matomo/blob/master/docker-compose.yml). 71 | 72 | If you want to use the import logs script, you can then run the following container as needed, in order to execute the python import logs script: 73 | 74 | ``` 75 | docker run --rm --volumes-from="matomo_app_1" --link matomo_app_1 python:2-alpine python /var/www/html/misc/log-analytics/import_logs.py --url=http://ip.of.your.matomo --login=yourlogin --password=yourpassword --idsite=1 --recorders=4 /var/www/html/logs/access.log 76 | ``` 77 | 78 | ## Contribute 79 | 80 | Pull requests are very welcome! 81 | 82 | We'd love to hear your feedback and suggestions in the issue tracker: [github.com/matomo-org/docker/issues](https://github.com/matomo-org/docker/issues). 83 | 84 | ## GeoIP 85 | 86 | ~~This product includes GeoLite data created by MaxMind, available from [https://www.maxmind.com](https://www.maxmind.com).~~ 87 | https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases/ 88 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | ## Security Bug Bounty Program 4 | 5 | The [Matomo Security Bug Bounty Program on HackerOne](https://hackerone.com/matomo/) is designed to encourage security research in Matomo software and to reward those who help us create the safest web analytics platform. 6 | 7 | ## Responsible disclosure by email 8 | 9 | We encourage you to responsibly report issues via our [Matomo Bug Bounty Program on HackerOne](https://hackerone.com/matomo) or you can also 10 | [email us at security@matomo.org](mailto:security@matomo.org?subject=Reporting%20Vulnerability%20in%20Matomo). 11 | 12 | If you have found a security issue in Matomo please read [our security notes](https://matomo.org/security/) regarding responsible disclosures. 13 | -------------------------------------------------------------------------------- /apache/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.3-apache 2 | 3 | ENV PHP_MEMORY_LIMIT=256M 4 | 5 | RUN set -ex; \ 6 | \ 7 | savedAptMark="$(apt-mark showmanual)"; \ 8 | \ 9 | apt-get update; \ 10 | apt-get install -y --no-install-recommends \ 11 | libfreetype-dev \ 12 | libjpeg-dev \ 13 | libldap2-dev \ 14 | libpng-dev \ 15 | libzip-dev \ 16 | procps \ 17 | ; \ 18 | \ 19 | debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ 20 | docker-php-ext-configure gd --with-freetype --with-jpeg; \ 21 | docker-php-ext-configure ldap --with-libdir="lib/$debMultiarch"; \ 22 | docker-php-ext-install -j "$(nproc)" \ 23 | gd \ 24 | bcmath \ 25 | ldap \ 26 | mysqli \ 27 | opcache \ 28 | pdo_mysql \ 29 | zip \ 30 | ; \ 31 | \ 32 | # pecl will claim success even if one install fails, so we need to perform each install separately 33 | pecl install APCu-5.1.24; \ 34 | pecl install redis-6.1.0; \ 35 | \ 36 | docker-php-ext-enable \ 37 | apcu \ 38 | redis \ 39 | ; \ 40 | rm -r /tmp/pear; \ 41 | \ 42 | # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies 43 | apt-mark auto '.*' > /dev/null; \ 44 | apt-mark manual $savedAptMark; \ 45 | ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \ 46 | | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ 47 | | sort -u \ 48 | | xargs -r dpkg-query --search \ 49 | | cut -d: -f1 \ 50 | | sort -u \ 51 | | xargs -rt apt-mark manual; \ 52 | \ 53 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ 54 | rm -rf /var/lib/apt/lists/* 55 | 56 | # set recommended PHP.ini settings 57 | # see https://secure.php.net/manual/en/opcache.installation.php 58 | RUN { \ 59 | echo 'opcache.memory_consumption=128'; \ 60 | echo 'opcache.interned_strings_buffer=8'; \ 61 | echo 'opcache.max_accelerated_files=4000'; \ 62 | echo 'opcache.revalidate_freq=2'; \ 63 | echo 'opcache.fast_shutdown=1'; \ 64 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 65 | 66 | ENV MATOMO_VERSION 5.3.2 67 | 68 | RUN set -ex; \ 69 | fetchDeps=" \ 70 | dirmngr \ 71 | gnupg \ 72 | "; \ 73 | apt-get update; \ 74 | apt-get install -y --no-install-recommends \ 75 | $fetchDeps \ 76 | ; \ 77 | \ 78 | curl -fsSL -o matomo.tar.gz \ 79 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz"; \ 80 | curl -fsSL -o matomo.tar.gz.asc \ 81 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz.asc"; \ 82 | export GNUPGHOME="$(mktemp -d)"; \ 83 | gpg --batch --keyserver keyserver.ubuntu.com --recv-keys F529A27008477483777FC23D63BB30D0E5D2C749; \ 84 | gpg --batch --verify matomo.tar.gz.asc matomo.tar.gz; \ 85 | gpgconf --kill all; \ 86 | rm -rf "$GNUPGHOME" matomo.tar.gz.asc; \ 87 | tar -xzf matomo.tar.gz -C /usr/src/; \ 88 | rm matomo.tar.gz; \ 89 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $fetchDeps; \ 90 | rm -rf /var/lib/apt/lists/* 91 | 92 | COPY php.ini /usr/local/etc/php/conf.d/php-matomo.ini 93 | 94 | COPY docker-entrypoint.sh /entrypoint.sh 95 | 96 | # WORKDIR is /var/www/html (inherited via "FROM php") 97 | # "/entrypoint.sh" will populate it at container startup from /usr/src/matomo 98 | VOLUME /var/www/html 99 | 100 | ENTRYPOINT ["/entrypoint.sh"] 101 | CMD ["apache2-foreground"] 102 | -------------------------------------------------------------------------------- /apache/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # usage: file_env VAR [DEFAULT] 5 | # ie: file_env 'XYZ_DB_PASSWORD' 'example' 6 | # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 7 | # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 8 | file_env() { 9 | local var="$1" 10 | local fileVar="${var}_FILE" 11 | local def="${2:-}" 12 | local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") 13 | local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") 14 | if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then 15 | echo >&2 "error: both $var and $fileVar are set (but are exclusive)" 16 | exit 1 17 | fi 18 | if [ -n "${varValue}" ]; then 19 | export "$var"="${varValue}" 20 | elif [ -n "${fileVarValue}" ]; then 21 | export "$var"="$(cat "${fileVarValue}")" 22 | elif [ -n "${def}" ]; then 23 | export "$var"="$def" 24 | fi 25 | unset "$fileVar" 26 | } 27 | 28 | file_env 'MATOMO_DATABASE_HOST' 29 | file_env 'MATOMO_DATABASE_USERNAME' 30 | file_env 'MATOMO_DATABASE_PASSWORD' 31 | file_env 'MATOMO_DATABASE_DBNAME' 32 | 33 | if [ ! -e matomo.php ]; then 34 | uid="$(id -u)" 35 | gid="$(id -g)" 36 | if [ "$uid" = '0' ]; then 37 | case "$1" in 38 | apache2*) 39 | user="${APACHE_RUN_USER:-www-data}" 40 | group="${APACHE_RUN_GROUP:-www-data}" 41 | 42 | # strip off any '#' symbol ('#1000' is valid syntax for Apache) 43 | user="${user#'#'}" 44 | group="${group#'#'}" 45 | ;; 46 | *) # php-fpm 47 | user='www-data' 48 | group='www-data' 49 | ;; 50 | esac 51 | else 52 | user="$uid" 53 | group="$gid" 54 | fi 55 | tar cf - --one-file-system -C /usr/src/matomo . | tar xf - 56 | chown -R "$user":"$group" . 57 | fi 58 | 59 | exec "$@" 60 | -------------------------------------------------------------------------------- /apache/php.ini: -------------------------------------------------------------------------------- 1 | always_populate_raw_post_data=-1 2 | geoip.custom_directory=/var/www/html/misc 3 | display_errors=Off 4 | memory_limit=${PHP_MEMORY_LIMIT} 5 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # usage: file_env VAR [DEFAULT] 5 | # ie: file_env 'XYZ_DB_PASSWORD' 'example' 6 | # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 7 | # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 8 | file_env() { 9 | local var="$1" 10 | local fileVar="${var}_FILE" 11 | local def="${2:-}" 12 | local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") 13 | local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") 14 | if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then 15 | echo >&2 "error: both $var and $fileVar are set (but are exclusive)" 16 | exit 1 17 | fi 18 | if [ -n "${varValue}" ]; then 19 | export "$var"="${varValue}" 20 | elif [ -n "${fileVarValue}" ]; then 21 | export "$var"="$(cat "${fileVarValue}")" 22 | elif [ -n "${def}" ]; then 23 | export "$var"="$def" 24 | fi 25 | unset "$fileVar" 26 | } 27 | 28 | file_env 'MATOMO_DATABASE_HOST' 29 | file_env 'MATOMO_DATABASE_USERNAME' 30 | file_env 'MATOMO_DATABASE_PASSWORD' 31 | file_env 'MATOMO_DATABASE_DBNAME' 32 | 33 | if [ ! -e matomo.php ]; then 34 | uid="$(id -u)" 35 | gid="$(id -g)" 36 | if [ "$uid" = '0' ]; then 37 | case "$1" in 38 | apache2*) 39 | user="${APACHE_RUN_USER:-www-data}" 40 | group="${APACHE_RUN_GROUP:-www-data}" 41 | 42 | # strip off any '#' symbol ('#1000' is valid syntax for Apache) 43 | user="${user#'#'}" 44 | group="${group#'#'}" 45 | ;; 46 | *) # php-fpm 47 | user='www-data' 48 | group='www-data' 49 | ;; 50 | esac 51 | else 52 | user="$uid" 53 | group="$gid" 54 | fi 55 | tar cf - --one-file-system -C /usr/src/matomo . | tar xf - 56 | chown -R "$user":"$group" . 57 | fi 58 | 59 | exec "$@" 60 | -------------------------------------------------------------------------------- /fpm-alpine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.3-fpm-alpine 2 | 3 | ENV PHP_MEMORY_LIMIT=256M 4 | 5 | RUN set -ex; \ 6 | \ 7 | apk add --no-cache --virtual .build-deps \ 8 | $PHPIZE_DEPS \ 9 | autoconf \ 10 | freetype-dev \ 11 | icu-dev \ 12 | libjpeg-turbo-dev \ 13 | libpng-dev \ 14 | libzip-dev \ 15 | openldap-dev \ 16 | pcre-dev \ 17 | procps \ 18 | ; \ 19 | \ 20 | docker-php-ext-configure gd --with-freetype --with-jpeg; \ 21 | docker-php-ext-configure ldap; \ 22 | docker-php-ext-install -j "$(nproc)" \ 23 | gd \ 24 | bcmath \ 25 | ldap \ 26 | mysqli \ 27 | opcache \ 28 | pdo_mysql \ 29 | zip \ 30 | ; \ 31 | \ 32 | # pecl will claim success even if one install fails, so we need to perform each install separately 33 | pecl install APCu-5.1.24; \ 34 | pecl install redis-6.1.0; \ 35 | \ 36 | docker-php-ext-enable \ 37 | apcu \ 38 | redis \ 39 | ; \ 40 | rm -r /tmp/pear; \ 41 | \ 42 | runDeps="$( \ 43 | scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ 44 | | tr ',' '\n' \ 45 | | sort -u \ 46 | | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ 47 | )"; \ 48 | apk add --no-network --virtual .matomo-phpext-rundeps $runDeps; \ 49 | apk del --no-network .build-deps 50 | 51 | # set recommended PHP.ini settings 52 | # see https://secure.php.net/manual/en/opcache.installation.php 53 | RUN { \ 54 | echo 'opcache.memory_consumption=128'; \ 55 | echo 'opcache.interned_strings_buffer=8'; \ 56 | echo 'opcache.max_accelerated_files=4000'; \ 57 | echo 'opcache.revalidate_freq=2'; \ 58 | echo 'opcache.fast_shutdown=1'; \ 59 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 60 | 61 | ENV MATOMO_VERSION 5.3.2 62 | 63 | RUN set -ex; \ 64 | apk add --no-cache --virtual .fetch-deps \ 65 | gnupg \ 66 | ; \ 67 | \ 68 | curl -fsSL -o matomo.tar.gz \ 69 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz"; \ 70 | curl -fsSL -o matomo.tar.gz.asc \ 71 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz.asc"; \ 72 | export GNUPGHOME="$(mktemp -d)"; \ 73 | gpg --batch --keyserver keyserver.ubuntu.com --recv-keys F529A27008477483777FC23D63BB30D0E5D2C749; \ 74 | gpg --batch --verify matomo.tar.gz.asc matomo.tar.gz; \ 75 | gpgconf --kill all; \ 76 | rm -rf "$GNUPGHOME" matomo.tar.gz.asc; \ 77 | tar -xzf matomo.tar.gz -C /usr/src/; \ 78 | rm matomo.tar.gz; \ 79 | apk del .fetch-deps 80 | 81 | COPY php.ini /usr/local/etc/php/conf.d/php-matomo.ini 82 | 83 | COPY docker-entrypoint.sh /entrypoint.sh 84 | 85 | # WORKDIR is /var/www/html (inherited via "FROM php") 86 | # "/entrypoint.sh" will populate it at container startup from /usr/src/matomo 87 | VOLUME /var/www/html 88 | 89 | ENTRYPOINT ["/entrypoint.sh"] 90 | CMD ["php-fpm"] 91 | -------------------------------------------------------------------------------- /fpm-alpine/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # usage: file_env VAR [DEFAULT] 5 | # ie: file_env 'XYZ_DB_PASSWORD' 'example' 6 | # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 7 | # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 8 | file_env() { 9 | local var="$1" 10 | local fileVar="${var}_FILE" 11 | local def="${2:-}" 12 | local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") 13 | local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") 14 | if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then 15 | echo >&2 "error: both $var and $fileVar are set (but are exclusive)" 16 | exit 1 17 | fi 18 | if [ -n "${varValue}" ]; then 19 | export "$var"="${varValue}" 20 | elif [ -n "${fileVarValue}" ]; then 21 | export "$var"="$(cat "${fileVarValue}")" 22 | elif [ -n "${def}" ]; then 23 | export "$var"="$def" 24 | fi 25 | unset "$fileVar" 26 | } 27 | 28 | file_env 'MATOMO_DATABASE_HOST' 29 | file_env 'MATOMO_DATABASE_USERNAME' 30 | file_env 'MATOMO_DATABASE_PASSWORD' 31 | file_env 'MATOMO_DATABASE_DBNAME' 32 | 33 | if [ ! -e matomo.php ]; then 34 | uid="$(id -u)" 35 | gid="$(id -g)" 36 | if [ "$uid" = '0' ]; then 37 | case "$1" in 38 | apache2*) 39 | user="${APACHE_RUN_USER:-www-data}" 40 | group="${APACHE_RUN_GROUP:-www-data}" 41 | 42 | # strip off any '#' symbol ('#1000' is valid syntax for Apache) 43 | user="${user#'#'}" 44 | group="${group#'#'}" 45 | ;; 46 | *) # php-fpm 47 | user='www-data' 48 | group='www-data' 49 | ;; 50 | esac 51 | else 52 | user="$uid" 53 | group="$gid" 54 | fi 55 | tar cf - --one-file-system -C /usr/src/matomo . | tar xf - 56 | chown -R "$user":"$group" . 57 | fi 58 | 59 | exec "$@" 60 | -------------------------------------------------------------------------------- /fpm-alpine/php.ini: -------------------------------------------------------------------------------- 1 | always_populate_raw_post_data=-1 2 | geoip.custom_directory=/var/www/html/misc 3 | display_errors=Off 4 | memory_limit=${PHP_MEMORY_LIMIT} 5 | -------------------------------------------------------------------------------- /fpm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.3-fpm 2 | 3 | ENV PHP_MEMORY_LIMIT=256M 4 | 5 | RUN set -ex; \ 6 | \ 7 | savedAptMark="$(apt-mark showmanual)"; \ 8 | \ 9 | apt-get update; \ 10 | apt-get install -y --no-install-recommends \ 11 | libfreetype-dev \ 12 | libjpeg-dev \ 13 | libldap2-dev \ 14 | libpng-dev \ 15 | libzip-dev \ 16 | procps \ 17 | ; \ 18 | \ 19 | debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ 20 | docker-php-ext-configure gd --with-freetype --with-jpeg; \ 21 | docker-php-ext-configure ldap --with-libdir="lib/$debMultiarch"; \ 22 | docker-php-ext-install -j "$(nproc)" \ 23 | gd \ 24 | bcmath \ 25 | ldap \ 26 | mysqli \ 27 | opcache \ 28 | pdo_mysql \ 29 | zip \ 30 | ; \ 31 | \ 32 | # pecl will claim success even if one install fails, so we need to perform each install separately 33 | pecl install APCu-5.1.24; \ 34 | pecl install redis-6.1.0; \ 35 | \ 36 | docker-php-ext-enable \ 37 | apcu \ 38 | redis \ 39 | ; \ 40 | rm -r /tmp/pear; \ 41 | \ 42 | # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies 43 | apt-mark auto '.*' > /dev/null; \ 44 | apt-mark manual $savedAptMark; \ 45 | ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \ 46 | | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ 47 | | sort -u \ 48 | | xargs -r dpkg-query --search \ 49 | | cut -d: -f1 \ 50 | | sort -u \ 51 | | xargs -rt apt-mark manual; \ 52 | \ 53 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ 54 | rm -rf /var/lib/apt/lists/* 55 | 56 | # set recommended PHP.ini settings 57 | # see https://secure.php.net/manual/en/opcache.installation.php 58 | RUN { \ 59 | echo 'opcache.memory_consumption=128'; \ 60 | echo 'opcache.interned_strings_buffer=8'; \ 61 | echo 'opcache.max_accelerated_files=4000'; \ 62 | echo 'opcache.revalidate_freq=2'; \ 63 | echo 'opcache.fast_shutdown=1'; \ 64 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini 65 | 66 | ENV MATOMO_VERSION 5.3.2 67 | 68 | RUN set -ex; \ 69 | fetchDeps=" \ 70 | dirmngr \ 71 | gnupg \ 72 | "; \ 73 | apt-get update; \ 74 | apt-get install -y --no-install-recommends \ 75 | $fetchDeps \ 76 | ; \ 77 | \ 78 | curl -fsSL -o matomo.tar.gz \ 79 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz"; \ 80 | curl -fsSL -o matomo.tar.gz.asc \ 81 | "https://builds.matomo.org/matomo-${MATOMO_VERSION}.tar.gz.asc"; \ 82 | export GNUPGHOME="$(mktemp -d)"; \ 83 | gpg --batch --keyserver keyserver.ubuntu.com --recv-keys F529A27008477483777FC23D63BB30D0E5D2C749; \ 84 | gpg --batch --verify matomo.tar.gz.asc matomo.tar.gz; \ 85 | gpgconf --kill all; \ 86 | rm -rf "$GNUPGHOME" matomo.tar.gz.asc; \ 87 | tar -xzf matomo.tar.gz -C /usr/src/; \ 88 | rm matomo.tar.gz; \ 89 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $fetchDeps; \ 90 | rm -rf /var/lib/apt/lists/* 91 | 92 | COPY php.ini /usr/local/etc/php/conf.d/php-matomo.ini 93 | 94 | COPY docker-entrypoint.sh /entrypoint.sh 95 | 96 | # WORKDIR is /var/www/html (inherited via "FROM php") 97 | # "/entrypoint.sh" will populate it at container startup from /usr/src/matomo 98 | VOLUME /var/www/html 99 | 100 | ENTRYPOINT ["/entrypoint.sh"] 101 | CMD ["php-fpm"] 102 | -------------------------------------------------------------------------------- /fpm/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # usage: file_env VAR [DEFAULT] 5 | # ie: file_env 'XYZ_DB_PASSWORD' 'example' 6 | # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 7 | # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 8 | file_env() { 9 | local var="$1" 10 | local fileVar="${var}_FILE" 11 | local def="${2:-}" 12 | local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") 13 | local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") 14 | if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then 15 | echo >&2 "error: both $var and $fileVar are set (but are exclusive)" 16 | exit 1 17 | fi 18 | if [ -n "${varValue}" ]; then 19 | export "$var"="${varValue}" 20 | elif [ -n "${fileVarValue}" ]; then 21 | export "$var"="$(cat "${fileVarValue}")" 22 | elif [ -n "${def}" ]; then 23 | export "$var"="$def" 24 | fi 25 | unset "$fileVar" 26 | } 27 | 28 | file_env 'MATOMO_DATABASE_HOST' 29 | file_env 'MATOMO_DATABASE_USERNAME' 30 | file_env 'MATOMO_DATABASE_PASSWORD' 31 | file_env 'MATOMO_DATABASE_DBNAME' 32 | 33 | if [ ! -e matomo.php ]; then 34 | uid="$(id -u)" 35 | gid="$(id -g)" 36 | if [ "$uid" = '0' ]; then 37 | case "$1" in 38 | apache2*) 39 | user="${APACHE_RUN_USER:-www-data}" 40 | group="${APACHE_RUN_GROUP:-www-data}" 41 | 42 | # strip off any '#' symbol ('#1000' is valid syntax for Apache) 43 | user="${user#'#'}" 44 | group="${group#'#'}" 45 | ;; 46 | *) # php-fpm 47 | user='www-data' 48 | group='www-data' 49 | ;; 50 | esac 51 | else 52 | user="$uid" 53 | group="$gid" 54 | fi 55 | tar cf - --one-file-system -C /usr/src/matomo . | tar xf - 56 | chown -R "$user":"$group" . 57 | fi 58 | 59 | exec "$@" 60 | -------------------------------------------------------------------------------- /fpm/php.ini: -------------------------------------------------------------------------------- 1 | always_populate_raw_post_data=-1 2 | geoip.custom_directory=/var/www/html/misc 3 | display_errors=Off 4 | memory_limit=${PHP_MEMORY_LIMIT} 5 | -------------------------------------------------------------------------------- /generate-stackbrew-library.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | self="$(basename "$BASH_SOURCE")" 5 | cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" 6 | 7 | # Get the most recent commit which modified any of "$@". 8 | fileCommit() { 9 | git log -1 --format='format:%H' HEAD -- "$@" 10 | } 11 | 12 | # Get the most recent commit which modified "$1/Dockerfile" or any file that 13 | # the Dockerfile copies into the rootfs (with COPY). 14 | dockerfileCommit() { 15 | local dir="$1"; shift 16 | ( 17 | cd "$dir"; 18 | fileCommit Dockerfile \ 19 | $(git show HEAD:./Dockerfile | awk ' 20 | toupper($1) == "COPY" { 21 | for (i = 2; i < NF; i++) 22 | print $i; 23 | } 24 | ') 25 | ) 26 | } 27 | 28 | getArches() { 29 | local repo="$1"; shift 30 | local officialImagesUrl='https://github.com/docker-library/official-images/raw/master/library/' 31 | 32 | eval "declare -g -A parentRepoToArches=( $( 33 | find -name 'Dockerfile' -exec awk ' 34 | toupper($1) == "FROM" && $2 !~ /^('"$repo"'|scratch|microsoft\/[^:]+)(:|$)/ { 35 | print "'"$officialImagesUrl"'" $2 36 | } 37 | ' '{}' + \ 38 | | sort -u \ 39 | | xargs bashbrew cat --format '[{{ .RepoName }}:{{ .TagName }}]="{{ join " " .TagEntry.Architectures }}"' 40 | ) )" 41 | } 42 | getArches 'matomo' 43 | 44 | # Header. 45 | cat <<-EOH 46 | # This file is generated via https://github.com/matomo-org/docker/blob/$(fileCommit "$self")/$self 47 | Maintainers: Matomo Community (@matomo-org) 48 | GitRepo: https://github.com/matomo-org/docker.git 49 | EOH 50 | 51 | # prints "$2$1$3$1...$N" 52 | join() { 53 | local sep="$1"; shift 54 | local out; printf -v out "${sep//%/%%}%s" "$@" 55 | echo "${out#$sep}" 56 | } 57 | 58 | latest="$( 59 | git ls-remote --sort=version:refname --tags https://github.com/matomo-org/matomo.git \ 60 | | cut -d/ -f3 \ 61 | | grep -vE -- '-rc|-b|-a' \ 62 | | tail -1 63 | )" 64 | 65 | variants=( */ ) 66 | variants=( "${variants[@]%/}" ) 67 | 68 | for variant in "${variants[@]}"; do 69 | commit="$(dockerfileCommit "$variant")" 70 | fullversion="$(git show "$commit":"$variant/Dockerfile" | awk '$1 == "ENV" && $2 == "MATOMO_VERSION" { print $3; exit }')" 71 | 72 | versionAliases=( "$fullversion" "${fullversion%.*}" "${fullversion%.*.*}" ) 73 | if [ "$fullversion" = "$latest" ]; then 74 | versionAliases+=( "latest" ) 75 | fi 76 | 77 | variantAliases=( "${versionAliases[@]/%/-$variant}" ) 78 | variantAliases=( "${variantAliases[@]//latest-}" ) 79 | 80 | if [ "$variant" = "apache" ]; then 81 | variantAliases+=( "${versionAliases[@]}" ) 82 | fi 83 | 84 | variantParent="$(awk 'toupper($1) == "FROM" { print $2 }' "$variant/Dockerfile")" 85 | variantArches="${parentRepoToArches[$variantParent]}" 86 | 87 | cat <<-EOE 88 | 89 | Tags: $(join ', ' "${variantAliases[@]}") 90 | Architectures: $(join ', ' $variantArches) 91 | GitCommit: $commit 92 | Directory: $variant 93 | EOE 94 | done 95 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | View [license information](https://github.com/matomo-org/matomo/blob/master/LEGALNOTICE) for the software contained in this image. 2 | -------------------------------------------------------------------------------- /php.ini: -------------------------------------------------------------------------------- 1 | always_populate_raw_post_data=-1 2 | geoip.custom_directory=/var/www/html/misc 3 | display_errors=Off 4 | memory_limit=${PHP_MEMORY_LIMIT} 5 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | declare -A cmd=( 5 | [apache]='apache2-foreground' 6 | [fpm]='php-fpm' 7 | [fpm-alpine]='php-fpm' 8 | ) 9 | 10 | declare -A base=( 11 | [apache]='debian' 12 | [fpm]='debian' 13 | [fpm-alpine]='alpine' 14 | ) 15 | 16 | latest="$( 17 | git ls-remote --tags https://github.com/matomo-org/matomo.git \ 18 | | cut -d/ -f3 \ 19 | | grep -vE -- '-rc|-a|-b' \ 20 | | sort -V \ 21 | | tail -1 22 | )" 23 | 24 | set -x 25 | 26 | for variant in apache fpm fpm-alpine; do 27 | template="Dockerfile-${base[$variant]}.template" 28 | cp $template "$variant/Dockerfile" 29 | cp docker-entrypoint.sh "$variant/docker-entrypoint.sh" 30 | cp php.ini "$variant/php.ini" 31 | sed -ri -e ' 32 | s/%%VARIANT%%/'"$variant"'/; 33 | s/%%VERSION%%/'"$latest"'/; 34 | s/%%CMD%%/'"${cmd[$variant]}"'/; 35 | ' "$variant/Dockerfile" 36 | done 37 | --------------------------------------------------------------------------------