> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Plausible

> Deploy Plausible Community Edition on Control Plane. Cookie-free, privacy-friendly web analytics as a single-replica workload with a bundled PostgreSQL and single-node ClickHouse, private until you publish it. Needs an existing single-location GVC and three prerequisite secrets.

## Overview

Plausible Community Edition is lightweight, cookie-free web analytics (AGPL-3.0). One Plausible workload serves the dashboard, the tracker script and the event API on one port. Events and sessions are stored in a bundled single-node ClickHouse, and accounts, sites and goals in a bundled PostgreSQL. The install is private by default: you register the owner account first, then publish it.

> [!NOTE]
>
> This template deploys into an existing [GVC](https://docs.controlplane.com/reference/gvc.md) that you already have — it does not create one. The GVC must have exactly one location, and `clickhouse.locations[0].name` must be that location.

### What Gets Created

| Resource | Name | Purpose | Optional |
| - | - | - | - |
| Workload | `RELEASE_NAME-plausible` | Plausible server: dashboard, `/js/*` tracker scripts and `POST /api/event` on port `8000` (standard, 1 replica, container `plausible`) | — |
| Identity & Policy | `RELEASE_NAME-plausible-identity`, `RELEASE_NAME-plausible-policy`, `RELEASE_NAME-plausible-gvc-policy` | `reveal` on exactly the secrets Plausible reads, and `view` on its own GVC | — |
| Workload | `RELEASE_NAME-postgres` | PostgreSQL from the bundled [postgres](https://docs.controlplane.com/template-catalog/templates/postgres.md) subchart: users, teams, sites, goals (stateful) | — |
| Volume Set | `RELEASE_NAME-pg-vs` | PostgreSQL data | — |
| Identity & Policy | `RELEASE_NAME-pg-identity`, `RELEASE_NAME-pg-policy` | `reveal` on your PostgreSQL credentials secret | — |
| Workload | `RELEASE_NAME-clickhouse-server` | ClickHouse from the bundled [clickhouse](https://docs.controlplane.com/template-catalog/templates/clickhouse.md) subchart: every event and session (stateful, 1 replica, local storage) | — |
| Volume Set | `RELEASE_NAME-clickhouse-server-vs` | ClickHouse data | — |
| Secret | `RELEASE_NAME-clickhouse-server-startup` | ClickHouse startup script | — |
| Identity & Policy | `RELEASE_NAME-clickhouse-identity`, `RELEASE_NAME-clickhouse-policy`, `RELEASE_NAME-clickhouse-gvc-policy` | `reveal` on your ClickHouse credentials secret, and `view` on the GVC | — |
| Workload | `RELEASE_NAME-postgres-backup` | Cron job running scheduled PostgreSQL backups | `postgres.backup.enabled: true` |

## Prerequisites

Three secrets must exist **before** you install; the chart never creates them. They hold credentials, so they are prerequisite secrets rather than values. Secrets are org-level, so no GVC flag is involved.

**1. Signing key**

An [opaque secret](https://docs.controlplane.com/guides/create-secret/opaque.md) of at least 32 bytes. It signs sessions and derives the two-factor encryption key, so keep it for the life of the install:

```bash theme={null}
printf '%s' "$(openssl rand -base64 48)" | cpln secret create-opaque --name KEY_SECRET_NAME --encoding plain -f -
```

Set `app.secretKeyBaseSecretName` to this name.

**2. PostgreSQL credentials**

A [dictionary secret](https://docs.controlplane.com/guides/create-secret/dictionary.md) with keys `username`, `password` and `database`. Use URL-safe values, because Plausible puts them in its connection URL:

```bash theme={null}
cpln secret create-dictionary --name DB_SECRET_NAME \
  --entry username=plausible \
  --entry password="$(openssl rand -hex 24)" \
  --entry database=plausible
```

Set `postgres.config.credentialsSecretName` to this name.

**3. ClickHouse credentials**

A dictionary secret with `password` (URL-safe, no quotes) and `database` (a bare identifier):

```bash theme={null}
cpln secret create-dictionary --name CLICKHOUSE_SECRET_NAME \
  --entry password="$(openssl rand -hex 24)" \
  --entry database=plausible_events_db
```

Set `clickhouse.database.credentialsSecretName` to this name.

> [!WARNING]
>
> If a prerequisite secret is missing, the install reports success and then waits silently: `cpln logs` shows nothing, and the missing secret is named only in `status.versions[].message` of `cpln workload get-deployments RELEASE_NAME-plausible --gvc GVC_NAME -o yaml`. A secret created after the install is not picked up on its own: run `cpln workload force-redeployment RELEASE_NAME-plausible --gvc GVC_NAME` for every workload whose message names it (`RELEASE_NAME-clickhouse-server` for the ClickHouse secret).

Optional: an SMTP relay (see [Email](#email)) and a bucket with a cloud account for PostgreSQL backups (see [Backing Up](#backing-up)).

## Installation

A default install needs the three secret names and the location of your GVC:

```bash theme={null}
cpln helm install RELEASE_NAME oci://ghcr.io/controlplane-com/templates/plausible \
  --version 1.0.0 --gvc GVC_NAME \
  --set app.secretKeyBaseSecretName=KEY_SECRET_NAME \
  --set postgres.config.credentialsSecretName=DB_SECRET_NAME \
  --set clickhouse.database.credentialsSecretName=CLICKHOUSE_SECRET_NAME \
  --set 'clickhouse.locations[0].name=GVC_LOCATION' \
  --set 'clickhouse.locations[0].replicas=1'
```

Or install using your preferred method:

> [card] [UI](https://docs.controlplane.com/template-catalog/install-manage/ui.md)
>
> Browse, install, and manage templates visually

> [card] [CLI](https://docs.controlplane.com/template-catalog/install-manage/cli.md)
>
> Manage templates from your terminal

> [card] [Terraform](https://docs.controlplane.com/template-catalog/install-manage/terraform.md)
>
> Declare templates in your Terraform configurations

> [card] [Pulumi](https://docs.controlplane.com/template-catalog/install-manage/pulumi.md)
>
> Declare templates in your Pulumi programs

## Configuration

### Application

```yaml theme={null}
image: ghcr.io/plausible/community-edition:v3.2.1 # v3.2.1 or newer: the chart refuses older tags (CVE-2026-8467)

resources:
  minCpu: 250m
  maxCpu: 1
  minMemory: 512Mi
  maxMemory: 1Gi

app:
  secretKeyBaseSecretName: my-plausible-secret-key-base
  registration: invite_only # invite_only | true (closed) | false (open to anyone)
  baseUrl: ""               # "" = derived; custom domain: https://stats.example.com
```

* **`registration`**: the first account can always register. After that, `invite_only` admits only users invited from the dashboard.
* **`baseUrl`**: Plausible builds links from it and accepts browser connections only from its host. Empty means the canonical endpoint when public access is on, otherwise `http://localhost:8000` (the port-forward address). Set it, with `https://`, when you serve Plausible on your own domain.

### Email

Without SMTP, Plausible sends no invitations, password resets or email reports.

```yaml theme={null}
# example: the default is enabled: false with empty host and fromEmail
smtp:
  enabled: true
  host: smtp.example.com
  port: 587                 # 465 = implicit TLS; other ports use STARTTLS when the relay offers it
  fromEmail: analytics@example.com
  fromName: Plausible
  credentialsSecretName: my-plausible-smtp # dictionary with username and password; "" = no relay auth
```

Create the relay credentials secret before installing with it set:

```bash theme={null}
cpln secret create-dictionary --name SECRET_NAME \
  --entry username=smtp-username \
  --entry password='YOUR-SMTP-PASSWORD'
```

### Access

```yaml theme={null}
publicAccess:
  enabled: false # true = dashboard, tracker script and /api/event on the canonical *.cpln.app endpoint

internalAccess:
  type: same-gvc # options: none, same-gvc, same-org, workload-list
  workloads: []  # used with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME
```

### Bundled Stores

```yaml theme={null}
postgres:
  image: postgres:18
  config:
    credentialsSecretName: my-plausible-db-credentials
  resources:
    minCpu: 200m
    maxCpu: 500m
    minMemory: 256Mi
    maxMemory: 512Mi
  volumeset:
    capacity: 10 # GiB
  backup:
    enabled: false # true = scheduled backups of accounts, sites and goals (not events)
    schedule: "0 2 * * *"
    provider: aws  # aws, gcp, or minio
    aws:
      bucket: my-backup-bucket
      region: us-east-1
      cloudAccountName: my-backup-cloudaccount
      policyName: my-backup-policy

clickhouse:
  provider: local
  locations:
    - name: aws-us-east-1 # the one location of your GVC
      replicas: 1
  database:
    credentialsSecretName: my-plausible-clickhouse-credentials
  server:
    image: clickhouse/clickhouse-server:25.11
    resources:
      cpu: 1
      memory: 2Gi
  volumeset:
    server:
      capacity: 10 # GiB; holds all analytics data
```

Every knob of the [postgres](https://docs.controlplane.com/template-catalog/templates/postgres.md) template is available under `postgres`. ClickHouse is limited to one location with one replica, and stores its data on its volume, so no bucket is needed.

## Connecting

| What | Value |
| - | - |
| Local access (public access off) | `cpln port-forward RELEASE_NAME-plausible 8000:8000 --gvc GVC_NAME`, then open `http://localhost:8000` |
| Public URL | `status.canonicalEndpoint` from `cpln workload get RELEASE_NAME-plausible --gvc GVC_NAME -o yaml`, when `publicAccess.enabled: true` |
| Tracker script | `https://CANONICAL_ENDPOINT/js/script.js`; the dashboard shows the snippet for each site |
| Event API | `https://CANONICAL_ENDPOINT/api/event` |
| In-GVC | `http://RELEASE_NAME-plausible.GVC_NAME.cpln.local:8000`, subject to `internalAccess.type` |
| ClickHouse (raw events) | `http://RELEASE_NAME-clickhouse-server.GVC_NAME.cpln.local:8123`, user `default` with the password from your ClickHouse secret (`cpln secret reveal SECRET_NAME -o yaml`) |

Once a site exists in the dashboard, send a test pageview to the event API (the `domain` must match the site you added):

```bash theme={null}
curl -i -X POST https://CANONICAL_ENDPOINT/api/event \
  -H 'Content-Type: application/json' \
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36' \
  -d '{"name":"pageview","url":"https://example.com/","domain":"example.com"}'
```

A `202` response means the event was accepted; it then appears in the site's dashboard. Plausible drops events from bot-like user agents and events whose `domain` does not exactly match a site you added, and still answers `202`, so use a browser user agent and the domain as entered.

### First Run

Until the first account exists, anyone who can open Plausible can register as its owner. Register over a private tunnel first:

1. Install with `publicAccess.enabled: false` (the default).
2. Run `cpln port-forward RELEASE_NAME-plausible 8000:8000 --gvc GVC_NAME` and open `http://localhost:8000`. Plausible redirects to `/register`; create the owner account and add your first site.
3. Run `cpln helm upgrade` with `publicAccess.enabled: true`. Firewall changes take a few minutes to propagate.
4. Log in again at the canonical URL. Your account and sites are kept. Then add the tracker snippet to your site.

Use `http://localhost:8000` in step 2, not another host name: Plausible accepts browser connections only from the host of its base URL.

## Operations

### Backing Up

Backups are off by default. They cover PostgreSQL (accounts, sites, goals), not the analytics events in ClickHouse. Set `postgres.backup.enabled: true` and configure one provider. To restore, follow the restore steps on the [postgres](https://docs.controlplane.com/template-catalog/templates/postgres.md) page; analytics events in ClickHouse are not covered.

> [tab] AWS S3
>
> Create the bucket, a Control Plane [cloud account](https://docs.controlplane.com/guides/create-cloud-account.md), and an IAM policy scoped to the bucket, then set `postgres.backup.aws.bucket`, `region`, `cloudAccountName` and `policyName`:
>
> ```json theme={null}
> {
>   "Version": "2012-10-17",
>   "Statement": [
>     { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::my-backup-bucket" },
>     { "Effect": "Allow", "Action": ["s3:PutObject","s3:GetObject","s3:DeleteObject"], "Resource": "arn:aws:s3:::my-backup-bucket/*" }
>   ]
> }
> ```

> [tab] Google Cloud Storage
>
> Grant the cloud account's service account **Storage Object Admin** on the bucket, then set `postgres.backup.provider: gcp` and `postgres.backup.gcp.bucket` and `cloudAccountName`.

> [tab] MinIO or S3-compatible
>
> Create a dictionary secret with `accessKey` and `secretKey`, then set `postgres.backup.provider: minio` and `postgres.backup.minio.endpoint`, `bucket` and `credentialsSecretName`.

### Scaling and Availability

Plausible runs as one replica, and ClickHouse as one node in one location. Upgrades start the new Plausible replica before the old one stops. A restart of the replica is a short outage, and tracker hits during it are lost.

### Troubleshooting

> [accordion] Deployment never starts and shows no logs
>
> A prerequisite secret is missing. Read `status.versions[].message` with `cpln workload get-deployments RELEASE_NAME-plausible --gvc GVC_NAME -o yaml`, create the named secret, then run `cpln workload force-redeployment WORKLOAD_NAME --gvc GVC_NAME` for every workload whose message names it.

> [accordion] A FATAL about the location in the Plausible log
>
> `clickhouse.locations[0].name` is not your GVC's location, or the GVC has more than one location. Set it to the GVC's only location. Check the location with `cpln gvc get GVC_NAME -o yaml`.

> [accordion] Browser connection refused or redirected to the wrong host
>
> Plausible accepts browser connections only from the host of its base URL. Use `http://localhost:8000` through the port-forward, the canonical endpoint when public access is on, or set `app.baseUrl` to your custom domain.

> [accordion] No invitation or password-reset emails
>
> SMTP is off by default. Enable `smtp` and set its host, sender address and credentials secret.

## Important Notes

* **Register the owner before publishing.** Until the first account exists, anyone who can reach the URL can register as owner. See [First Run](#first-run).
* **Tracking needs `publicAccess.enabled: true`.** Visitors' browsers must reach the tracker script and `/api/event`.
* **The GVC needs exactly one location,** and `clickhouse.locations[0].name` must be it. A GVC with more than one location makes Plausible refuse to start.
* **Visitor locations are country-level**, from the DB-IP database bundled in the image.
* **Analytics events have no scheduled backup or snapshot.** They live only on the ClickHouse volume. PostgreSQL is backed up only when `postgres.backup.enabled` is on.
* **Uninstalling deletes both volumes.** Each keeps one final snapshot for 7 days; after that the data is gone.
* **Do not change the signing key.** A new one logs everyone out and breaks two-factor logins for every enrolled user.
* **Community Edition** has no funnels, ecommerce revenue goals, SSO or Sites API; those are Plausible Cloud features. The Stats API and shared dashboard links are available.

## External References

* [Plausible documentation](https://plausible.io/docs)
* [Community Edition repository](https://github.com/plausible/community-edition)
* [Community Edition configuration](https://github.com/plausible/community-edition/wiki/configuration)
* [Events API](https://plausible.io/docs/events-api)
