diff --git a/plausible/app.yaml b/plausible/app.yaml new file mode 100644 index 0000000..f6067d7 --- /dev/null +++ b/plausible/app.yaml @@ -0,0 +1,6 @@ +name: plausible +is: plausible +description: Privacy-friendly, open-source web analytics that doesn't require cookie banners or GDPR consent. +category: developer +icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/plausible.svg +latest: "3" diff --git a/plausible/notes.md b/plausible/notes.md new file mode 100644 index 0000000..8a32874 --- /dev/null +++ b/plausible/notes.md @@ -0,0 +1,38 @@ +# Plausible CE Notes + +## ClickHouse + +Plausible requires ClickHouse for analytics event storage. Unlike other app dependencies, ClickHouse is **bundled within the Plausible app** (not a separate Wild Cloud app). It runs in the same namespace with low-resource settings suitable for small/medium deployments. + +ClickHouse data is stored in the `clickhouse-data` PVC (default 10Gi). Resize `clickhouseStorage` in your config before deploying if you expect heavy traffic. + +## First Admin User + +The first user to register becomes the site admin. After deploying: +1. Open `https://plausible.` +2. Register an account +3. That account has admin access + +## Registration Control + +Set `disableRegistration` in config: +- `"false"` — open registration (default, good for initial setup) +- `"invite_only"` — admin can invite users +- `"true"` — no new registrations + +Set to `"invite_only"` or `"true"` after creating your admin account. + +## Email / SMTP + +Plausible can send email for password resets and notifications. Configure these manually in your instance's `secrets.yaml` and environment variables: +- `MAILER_ADAPTER`: `Bamboo.SMTPAdapter` +- `MAILER_EMAIL`: from address +- `SMTP_HOST_ADDR`, `SMTP_HOST_PORT`, `SMTP_USER_NAME`, `SMTP_USER_PWD` + +## Tracker Script + +Add the tracking script to your websites: + +```html + +``` diff --git a/plausible/versions/3/README.md b/plausible/versions/3/README.md new file mode 100644 index 0000000..3c3a7ce --- /dev/null +++ b/plausible/versions/3/README.md @@ -0,0 +1,49 @@ +# Plausible Analytics + +Plausible is a lightweight, privacy-friendly web analytics tool. It gives you a clear picture of how people find and use your websites — without tracking personal data, setting cookies, or requiring cookie consent banners. All data is stored on your own infrastructure. + +It's a straightforward alternative to Google Analytics for organizations that care about visitor privacy and data sovereignty. + +## Dependencies + +- **PostgreSQL** - Database for storing sites, users, and configuration +- **ClickHouse** - Bundled analytics event database (deployed automatically within the Plausible namespace) + +## Configuration + +Key settings configured through your instance's `config.yaml`: + +- **domain** - Where Plausible will be accessible (default: `plausible.{your-cloud-domain}`) +- **plausibleStorage** - Persistent volume for Plausible app data (default: `1Gi`) +- **clickhouseStorage** - Persistent volume for analytics event data (default: `10Gi`). Increase this if you expect high traffic volumes. +- **disableRegistration** - Controls who can create accounts: `"false"` (open), `"invite_only"`, or `"true"` (default: `"false"`) + +## Access + +After deployment, Plausible will be available at: +- `https://plausible.{your-cloud-domain}` — Dashboard and admin + +## First-Time Setup + +1. Add and deploy the app: + ```bash + wild app add plausible + wild app deploy plausible + ``` + +2. Open `https://plausible.{your-cloud-domain}` and register an account. The first registered user becomes the admin. + +3. Once your account is created, set `disableRegistration: "invite_only"` in your `config.yaml` to prevent open registration, then redeploy: + ```bash + wild app deploy plausible + ``` + +4. Add a website in the Plausible dashboard and copy the tracking snippet into your site's ``: + ```html + + ``` + +## Inviting Users + +With registration set to `invite_only`, you can invite teammates from **Settings → Users** in the Plausible dashboard. diff --git a/plausible/versions/3/configmap-clickhouse.yaml b/plausible/versions/3/configmap-clickhouse.yaml new file mode 100644 index 0000000..7777562 --- /dev/null +++ b/plausible/versions/3/configmap-clickhouse.yaml @@ -0,0 +1,58 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: clickhouse-config + labels: + component: clickhouse +data: + logs.xml: | + + + warning + true + + + + system + query_log
+ 7500 + + ENGINE = MergeTree + PARTITION BY event_date + ORDER BY (event_time) + TTL event_date + interval 30 day + SETTINGS ttl_only_drop_parts=1 + +
+ + + + + + + + + +
+ ipv4-only.xml: | + + 0.0.0.0 + + low-resources.xml: | + + + 524288000 + + default-profile-low-resources-overrides.xml: | + + + + + 1 + 8192 + 1 + 0 + 0 + + + diff --git a/plausible/versions/3/db-init-job.yaml b/plausible/versions/3/db-init-job.yaml new file mode 100644 index 0000000..16b1f71 --- /dev/null +++ b/plausible/versions/3/db-init-job.yaml @@ -0,0 +1,66 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: plausible-db-init + labels: + component: db-init +spec: + template: + metadata: + labels: + component: db-init + spec: + restartPolicy: OnFailure + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + seccompProfile: + type: RuntimeDefault + containers: + - name: postgres-init + image: postgres:16 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + env: + - name: PGHOST + value: {{ .db.host }} + - name: PGUSER + value: postgres + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: plausible-secrets + key: postgres.password + - name: DB_NAME + value: {{ .db.name }} + - name: DB_USER + value: {{ .db.user }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: plausible-secrets + key: dbPassword + command: + - /bin/bash + - -c + - | + set -e + echo "Waiting for PostgreSQL to be ready..." + until pg_isready; do + echo "PostgreSQL is not ready - sleeping" + sleep 2 + done + echo "PostgreSQL is ready" + + echo "Creating database and user for Plausible..." + psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" || echo "User ${DB_USER} already exists" + psql -c "ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';" + psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" || echo "Database ${DB_NAME} already exists" + psql -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};" + psql -d ${DB_NAME} -c "GRANT ALL ON SCHEMA public TO ${DB_USER};" + echo "Database initialization complete" diff --git a/plausible/versions/3/deployment-clickhouse.yaml b/plausible/versions/3/deployment-clickhouse.yaml new file mode 100644 index 0000000..b606376 --- /dev/null +++ b/plausible/versions/3/deployment-clickhouse.yaml @@ -0,0 +1,91 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: clickhouse + labels: + component: clickhouse +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + component: clickhouse + template: + metadata: + labels: + component: clickhouse + spec: + securityContext: + seccompProfile: + type: RuntimeDefault + containers: + - name: clickhouse + image: clickhouse/clickhouse-server:24.12-alpine + ports: + - name: http + containerPort: 8123 + protocol: TCP + - name: native + containerPort: 9000 + protocol: TCP + env: + - name: CLICKHOUSE_SKIP_USER_SETUP + value: "1" + volumeMounts: + - name: clickhouse-data + mountPath: /var/lib/clickhouse + - name: clickhouse-config + mountPath: /etc/clickhouse-server/config.d/logs.xml + subPath: logs.xml + readOnly: true + - name: clickhouse-config + mountPath: /etc/clickhouse-server/config.d/ipv4-only.xml + subPath: ipv4-only.xml + readOnly: true + - name: clickhouse-config + mountPath: /etc/clickhouse-server/config.d/low-resources.xml + subPath: low-resources.xml + readOnly: true + - name: clickhouse-users-config + mountPath: /etc/clickhouse-server/users.d/default-profile-low-resources-overrides.xml + subPath: default-profile-low-resources-overrides.xml + readOnly: true + readinessProbe: + httpGet: + path: /ping + port: 8123 + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + livenessProbe: + httpGet: + path: /ping + port: 8123 + initialDelaySeconds: 30 + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 3 + resources: + limits: + cpu: "2" + memory: 2Gi + ephemeral-storage: 1Gi + requests: + cpu: 100m + memory: 512Mi + ephemeral-storage: 50Mi + securityContext: + readOnlyRootFilesystem: false + volumes: + - name: clickhouse-data + persistentVolumeClaim: + claimName: clickhouse-data + - name: clickhouse-config + configMap: + name: clickhouse-config + - name: clickhouse-users-config + configMap: + name: clickhouse-config + restartPolicy: Always diff --git a/plausible/versions/3/deployment.yaml b/plausible/versions/3/deployment.yaml new file mode 100644 index 0000000..21bee16 --- /dev/null +++ b/plausible/versions/3/deployment.yaml @@ -0,0 +1,121 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: plausible + labels: + component: web +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + component: web + template: + metadata: + labels: + component: web + spec: + securityContext: + seccompProfile: + type: RuntimeDefault + initContainers: + - name: init-dirs + image: busybox:1.37 + command: + - /bin/sh + - -c + - | + mkdir -p /data/tmp /data/tzdata_data/release_ets + chmod -R 777 /data/tmp /data/tzdata_data + securityContext: + runAsUser: 0 + volumeMounts: + - name: plausible-data + mountPath: /data + - name: wait-for-clickhouse + image: busybox:1.37 + command: + - /bin/sh + - -c + - | + until wget --no-verbose --tries=1 -O - http://{{ .clickhouse.host }}:{{ .clickhouse.port }}/ping; do + echo "Waiting for ClickHouse..." + sleep 3 + done + securityContext: + runAsUser: 65534 + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + containers: + - name: plausible + image: ghcr.io/plausible/community-edition:v3.2.1 + command: + - sh + - -c + - /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run + ports: + - name: http + containerPort: 8000 + protocol: TCP + env: + - name: BASE_URL + value: https://{{ .domain }} + - name: TMPDIR + value: /var/lib/plausible/tmp + - name: DISABLE_REGISTRATION + value: "{{ .disableRegistration }}" + - name: SECRET_KEY_BASE + valueFrom: + secretKeyRef: + name: plausible-secrets + key: secretKeyBase + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: plausible-secrets + key: dbUrl + - name: CLICKHOUSE_DATABASE_URL + value: http://{{ .clickhouse.host }}:{{ .clickhouse.port }}/{{ .clickhouse.db }} + volumeMounts: + - name: plausible-data + mountPath: /var/lib/plausible + readinessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + livenessProbe: + httpGet: + path: / + port: 8000 + initialDelaySeconds: 60 + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 3 + resources: + limits: + cpu: "1" + memory: 1Gi + ephemeral-storage: 1Gi + requests: + cpu: 100m + memory: 256Mi + ephemeral-storage: 50Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + volumes: + - name: plausible-data + persistentVolumeClaim: + claimName: plausible-data + restartPolicy: Always diff --git a/plausible/versions/3/ingress.yaml b/plausible/versions/3/ingress.yaml new file mode 100644 index 0000000..c167041 --- /dev/null +++ b/plausible/versions/3/ingress.yaml @@ -0,0 +1,28 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: plausible + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + traefik.ingress.kubernetes.io/router.middlewares: plausible-private-network-access@kubernetescrd + external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }} + external-dns.alpha.kubernetes.io/cloudflare-proxied: "false" + external-dns.alpha.kubernetes.io/ttl: "60" +spec: + ingressClassName: traefik + tls: + - hosts: + - {{ .domain }} + secretName: {{ .tlsSecretName }} + rules: + - host: {{ .domain }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: plausible + port: + number: 80 diff --git a/plausible/versions/3/kustomization.yaml b/plausible/versions/3/kustomization.yaml new file mode 100644 index 0000000..e3df0dc --- /dev/null +++ b/plausible/versions/3/kustomization.yaml @@ -0,0 +1,20 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: plausible +labels: + - includeSelectors: true + pairs: + app: plausible + managedBy: kustomize + partOf: wild-cloud +resources: + - namespace.yaml + - db-init-job.yaml + - configmap-clickhouse.yaml + - pvc.yaml + - deployment-clickhouse.yaml + - service-clickhouse.yaml + - deployment.yaml + - service.yaml + - middleware.yaml + - ingress.yaml diff --git a/plausible/versions/3/manifest.yaml b/plausible/versions/3/manifest.yaml new file mode 100644 index 0000000..7e82a90 --- /dev/null +++ b/plausible/versions/3/manifest.yaml @@ -0,0 +1,33 @@ +version: v3.2.1-1 +requires: + - name: postgres +defaultConfig: + namespace: plausible + externalDnsDomain: '{{ .cloud.domain }}' + domain: plausible.{{ .cloud.domain }} + tlsSecretName: wildcard-wild-cloud-tls + plausibleStorage: 1Gi + clickhouseStorage: 10Gi + db: + host: '{{ .apps.postgres.host }}' + port: '{{ .apps.postgres.port }}' + name: plausible + user: plausible + clickhouse: + host: clickhouse + port: "8123" + db: plausible_events_db + disableRegistration: "false" + central: + headers: + response: + Access-Control-Allow-Private-Network: 'true' +defaultSecrets: + - key: dbPassword + - key: secretKeySeed + - key: secretKeyBase + default: '{{ crypto.SHA256 .secrets.secretKeySeed }}' + - key: dbUrl + default: 'postgresql://{{ .app.db.user }}:{{ .secrets.dbPassword }}@{{ .app.db.host }}:{{ .app.db.port }}/{{ .app.db.name }}?sslmode=disable' +requiredSecrets: + - postgres.password diff --git a/plausible/versions/3/middleware.yaml b/plausible/versions/3/middleware.yaml new file mode 100644 index 0000000..76db582 --- /dev/null +++ b/plausible/versions/3/middleware.yaml @@ -0,0 +1,8 @@ +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: private-network-access +spec: + headers: + customResponseHeaders: + Access-Control-Allow-Private-Network: "true" diff --git a/plausible/versions/3/namespace.yaml b/plausible/versions/3/namespace.yaml new file mode 100644 index 0000000..b7816b8 --- /dev/null +++ b/plausible/versions/3/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: "{{ .namespace }}" diff --git a/plausible/versions/3/pvc.yaml b/plausible/versions/3/pvc.yaml new file mode 100644 index 0000000..3f41eb5 --- /dev/null +++ b/plausible/versions/3/pvc.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: plausible-data +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .plausibleStorage }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: clickhouse-data +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .clickhouseStorage }} diff --git a/plausible/versions/3/service-clickhouse.yaml b/plausible/versions/3/service-clickhouse.yaml new file mode 100644 index 0000000..1f52f7a --- /dev/null +++ b/plausible/versions/3/service-clickhouse.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: clickhouse + labels: + component: clickhouse +spec: + type: ClusterIP + ports: + - port: 8123 + targetPort: http + protocol: TCP + name: http + - port: 9000 + targetPort: native + protocol: TCP + name: native + selector: + component: clickhouse diff --git a/plausible/versions/3/service.yaml b/plausible/versions/3/service.yaml new file mode 100644 index 0000000..cf3a4fa --- /dev/null +++ b/plausible/versions/3/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: plausible +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + component: web