cubeshipd/cubeship-plausible-template
Plausible
Plausible Community Edition: lightweight, privacy-friendly web analytics with ClickHouse
Plausible on Cubeship
Plausible is a simple, privacy-friendly web analytics tool: no cookies, no personal data, and a single-page dashboard. This template installs Plausible Community Edition, the self-hosted edition, on a Cubeship instance.
What it creates
- plausible — Plausible CE
v3.2.1, built on the instance fromplausible/Dockerfile. The dashboard and the tracking script answer on the domain you choose. - clickhouse — ClickHouse
24.12.6.70, built fromclickhouse/Dockerfile, where Plausible keeps every pageview and event. It has no domain, listens on port8123inside the instance, and keeps its data in a volume at/var/lib/clickhouse. - plausible-db — a managed Postgres 18 database, attached to Plausible: users, sites, goals and settings.
It needs Cubeship 0.7.0 or newer, and an admin to install it: both apps are built on the instance, and only admins build. ClickHouse also needs a CPU with SSE 4.2 (x86) or NEON (ARM), which any recent VPS has.
Why they are built
Both published images are used as they are, with what Plausible's own compose file adds to them and Cubeship cannot:
- Plausible — compose replaces the command with
db createdb && db migrate && run. The image's own command is onlyrun, which starts on an empty database and fails. The Dockerfile makes those three steps the command, so every start, and so every deploy, creates what is missing and migrates before the server starts. - ClickHouse — compose mounts four configuration files from
clickhouse/: logs to the console, the system logs Plausible never reads switched off, IPv4 only, and smaller caches and one thread per query for a small machine. Cubeship cannot mount a file, so the Dockerfile copies them in.
Compose also raises the open-files limit, which Cubeship cannot set on a
container. ClickHouse raises its own limit to the host's maximum when it
starts, and Plausible's command raises its limit to 65535 where the host
allows.
What you are asked
| Input | What to give |
|---|---|
| Where Plausible answers | A domain you control, pointed at your instance. It becomes BASE_URL, and the address of the tracking script. |
| The key sessions are signed with | Nothing — the instance generates it. |
| The key two-factor secrets are encrypted with | The output of openssl rand -base64 32. Keep a copy. |
| The password Plausible uses for ClickHouse | Nothing — the instance generates it. |
| Your SMTP server | The host your mail provider gives you, like smtp.mailgun.org. |
| Its port | 587 unless your provider says otherwise. |
| The SMTP username | From your mail provider. |
| The SMTP password | From your mail provider. |
| The address Plausible sends from | An address your provider lets you send as. |
The two-factor key is typed, not generated. Plausible reads it as Base64 of exactly 32 bytes, and the instance only generates letters and digits, which never decode to that. Changing it later makes every enabled two-factor setup unreadable.
Mail is not optional. Invites are the only way to add people once the first
account exists, and password resets and email reports need it too. Plausible
reads an empty variable as a value rather than as unset, so a blank SMTP answer
would stop it starting. On port 465 Plausible connects with TLS from the
first byte; on any other it connects in plain and relies on the server's
STARTTLS.
After installing
-
Open
https://<your domain>straight away. Plausible has no default account: while it has no users, it sends everyone to registration, and the first person to register owns the instance. -
Add your website, then paste the snippet Plausible shows into the
<head>of every page. It loads the script from your domain:<!-- Privacy-friendly analytics by Plausible --> <script async src="https://<your domain>/js/pa-XXXXXXXX.js"></script> <script> window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}}; plausible.init() </script>The
pa-…name is per site; copy it from the site's settings, not from here.
After the first account, registration is by invite only — Plausible's default.
To open or close it completely, set DISABLE_REGISTRATION on the plausible
app to false or true and redeploy.
Reaching the data
Cubeship has no console into an app. Anything that needs a shell is done over
SSH on the machine the app runs on, with docker exec. For example, to query
the events with ClickHouse's client and the generated password:
docker exec -it $(docker ps -qf name=cubeship-plausible-production-clickhouse) \
clickhouse-client --user plausible --password '<password>' \
--database plausible_events_db
The ClickHouse password is CLICKHOUSE_PASSWORD on the clickhouse app. The
internal names follow the project, environment and app names you install
with; they are on each app's page in the dashboard.
Countries and cities
Plausible ships a db-ip country database in its image, so visitor countries
work without any setup. For regions and cities, create a free
MaxMind account and set MAXMIND_LICENSE_KEY on the
plausible app, then redeploy; see Plausible's
MaxMind guide.
Plausible downloads the database into the container, which has no volume, so it
downloads it again after each deploy.
Google Search Console and Google Analytics imports need GOOGLE_CLIENT_ID and
GOOGLE_CLIENT_SECRET; see the
Google guide.
The volume
ClickHouse runs as one copy on the machine its volume is on, and a deploy stops it for a few seconds, during which Plausible cannot record events. Back up both the volume, for the statistics, and the database, for the accounts and sites they belong to.
Resources
ClickHouse is limited to 1 CPU and 2 GiB of memory. It reads the container's
limit and caps its own use at 90% of it, so a query that needs more fails with
a memory error rather than the container being killed. Plausible is limited to 1 CPU and 1 GiB. Plausible recommends at least 2 GiB
for the two together; raise ClickHouse's limits in template.yaml first for
busy sites or long date ranges.
Updating
Change the tag in plausible/Dockerfile, release this repository, and point
both apps' ref at the new release. Plausible migrates on start, and there is
no going back: back the database and the volume up first, and read the
release notes and
upgrade guide.
Move ClickHouse only to the version Plausible's compose file names for that
release.
What this creates
clickhouse
https://github.com/cubeshipd/cubeship-plausible-template
plausible
https://github.com/cubeshipd/cubeship-plausible-template
plausible-db
Postgres 18
/var/lib/clickhouse
Volume of clickhouse
# yaml-language-server: $schema=https://cubeship.dev/schema/template/v1.json
version: 1
# The first release that keeps a volume's data across deploys.
minCubeship: "0.7.0"
project: plausible
inputs:
- key: domain
type: domain
label: Where Plausible answers
- key: secretKeyBase
type: secret
label: The key sessions are signed with
generate: 64
- key: totpVaultKey
type: secret
label: The key two-factor secrets are encrypted with
help: Run `openssl rand -base64 32` and paste what it prints. Plausible needs Base64 of exactly 32 bytes, which a generated value cannot be.
- key: clickhousePassword
type: secret
label: The password Plausible uses for ClickHouse
generate: 32
- key: smtpHost
type: text
label: Your SMTP server
help: Invites, password resets and email reports all go out by mail.
- key: smtpPort
type: number
label: Its port
help: 587 for STARTTLS; 465 connects with TLS from the first byte.
default: 587
min: 1
max: 65535
- key: smtpUser
type: text
label: The SMTP username
- key: smtpPassword
type: secret
label: The SMTP password
- key: mailFrom
type: text
label: The address Plausible sends from
help: One your SMTP provider lets you send as, like plausible@example.com.
pattern: ^[^@\s]+@[^@\s]+\.[^@\s]+$
databases:
- key: db
name: plausible-db
# Plausible's CI tests against Postgres 18.
engine: postgres
version: "18"
database: plausible
apps:
- key: events
name: clickhouse
# The published image runs with its stock configuration; the Dockerfile
# adds the files Plausible's compose file mounts.
repo: https://github.com/cubeshipd/cubeship-plausible-template
ref: v1.0.0
build: dockerfile
dockerfile: clickhouse/Dockerfile
port: 8123
health: /ping
# No domain: only Plausible talks to it.
volumes:
- path: /var/lib/clickhouse
limits: { cpu: 1, memory: 2Gi }
env:
# Replaces the passwordless default user, which any app on the
# instance could otherwise reach.
CLICKHOUSE_USER: plausible
CLICKHOUSE_PASSWORD: ${input.clickhousePassword}
- key: web
name: plausible
# The published image starts without creating or migrating its
# databases; the Dockerfile runs both first, as upstream's compose does.
repo: https://github.com/cubeshipd/cubeship-plausible-template
ref: v1.0.0
build: dockerfile
dockerfile: plausible/Dockerfile
port: 8000
# /api/health also checks both databases, and would take the domain down
# whenever ClickHouse restarts.
health: /api/system/health/live
domains:
- host: ${input.domain}
attach:
- database: db
limits: { cpu: 1, memory: 1Gi }
env:
BASE_URL: https://${input.domain}
SECRET_KEY_BASE: ${input.secretKeyBase}
TOTP_VAULT_KEY: ${input.totpVaultKey}
CLICKHOUSE_DATABASE_URL: http://plausible:${input.clickhousePassword}@${app.events.internal}:${app.events.port}/plausible_events_db
MAILER_EMAIL: ${input.mailFrom}
SMTP_HOST_ADDR: ${input.smtpHost}
SMTP_HOST_PORT: ${input.smtpPort}
SMTP_USER_NAME: ${input.smtpUser}
SMTP_USER_PWD: ${input.smtpPassword}